¿Cómo ejecutar varias goroutines en Go para tareas concurrentes?
En el desarrollo con Go, es común encontrarse con la necesidad de procesar varias tareas al mismo tiempo, como la lectura de diferentes fuentes RSS. Aprender cómo implementar concurrencia en Go con goroutines y canales es una habilidad fundamental para aprovechar al máximo el potencial del lenguaje.
Supongamos que queremos obtener el contenido de varios archivos XML de feeds RSS. En este ejemplo, nuestro lector tiene una lista con tres direcciones URL:
- ESPN: http://www.espn.com/espn/rss/news
- NY Times: http://rss.nytimes.com/services/xml/rss/nyt/World.xml
- Wall Street Journal: https://feeds.a.dj.com/rss/RSSWorldNews.xml
El siguiente código muestra ejemplos prácticos de worker pools en Go y cómo lanzar varias rutinas para procesar cada fuente de manera simultánea:
package main
import (
"fmt"
"net/http"
"time"
)
type RSS struct {
Nombre string
Url string
}
func (rss *RSS) leer() {
t1 := time.Now()
resp, err := http.Get(rss.Url)
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
t2 := time.Since(t1).Seconds()
fmt.Println(rss.Nombre, "lectura completada en", t2, "segundos")
}
func main() {
listaRSS := []*RSS{
&RSS{Nombre: "ESPN", Url: "http://www.espn.com/espn/rss/news"},
&RSS{Nombre: "NY Times", Url: "http://rss.nytimes.com/services/xml/rss/nyt/World.xml"},
&RSS{Nombre: "Wall Street Journal", Url: "https://feeds.a.dj.com/rss/RSSWorldNews.xml"},
}
for _, rss := range listaRSS {
go rss.leer()
}
time.Sleep(time.Second * 6)
}
Primero, definimos una estructura que almacena el nombre y la URL de cada feed RSS:
type RSS struct {
Nombre string
Url string
}
Luego, implementamos un método que realiza la petición HTTP y mide el tiempo de respuesta, mostrando los resultados al finalizar la lectura:
func (rss *RSS) leer() {
t1 := time.Now()
resp, err := http.Get(rss.Url)
if err != nil {
fmt.Println(err)
}
defer resp.Body.Close()
t2 := time.Since(t1).Seconds()
fmt.Println(rss.Nombre, "lectura completada en", t2, "segundos")
}
Para gestionar varias fuentes, creamos un slice con las diferentes instancias de la estructura RSS:
listaRSS := []*RSS{
&RSS{Nombre: "ESPN", Url: "http://www.espn.com/espn/rss/news"},
&RSS{Nombre: "NY Times", Url: "http://rss.nytimes.com/services/xml/rss/nyt/World.xml"},
&RSS{Nombre: "Wall Street Journal", Url: "https://feeds.a.dj.com/rss/RSSWorldNews.xml"},
}
Finalmente, recorremos la lista y, para cada elemento, lanzamos una goroutine que ejecuta el método de lectura. Así, todas las tareas se procesan de forma concurrente, aprovechando las ventajas de usar goroutines para tareas simultáneas:
for _, rss := range listaRSS {
go rss.leer()
}
time.Sleep(time.Second * 6)
La salida muestra que cada rutina puede finalizar en tiempos distintos, dependiendo de la velocidad de respuesta de cada servidor. El tiempo total de ejecución suele ser cercano al de la tarea más lenta, lo que demuestra cómo manejar múltiples tareas concurrentes en Go de manera eficiente.
NY Times lectura completada en 0.056710012 segundos
ESPN lectura completada en 0.079235882 segundos
Wall Street Journal lectura completada en 0.4200502 segundos
Si ejecutas el script varias veces, notarás que el orden de finalización y los tiempos pueden variar, ya que cada petición se realiza de forma independiente y concurrente.
Conclusión
El uso de goroutines en Go permite ejecutar múltiples tareas de manera eficiente y sencilla, facilitando el desarrollo de aplicaciones concurrentes y escalables. Al aprovechar la concurrencia, es posible reducir los tiempos de espera y mejorar el rendimiento general de los programas, especialmente cuando se trata de operaciones de entrada/salida como la lectura de feeds RSS. Además, la sintaxis de Go hace que la implementación de rutinas concurrentes sea accesible incluso para quienes están comenzando en el lenguaje. Adoptar mejores prácticas para la concurrencia en Go y comprender cómo implementar concurrencia en Go con goroutines y canales son pasos clave para desarrollar soluciones robustas y modernas. En definitiva, el procesamiento simultáneo en Go con goroutines es una de las características más poderosas del lenguaje y una herramienta esencial para cualquier desarrollador que busque eficiencia y escalabilidad.
Cuestionario de repaso
- ¿Qué es una goroutine en Go y para qué se utiliza?
- ¿Cómo se define una estructura para almacenar información de un feed RSS en Go?
- ¿Por qué es importante el uso de canales y goroutines para la concurrencia en Go?
- ¿Qué ventaja principal ofrece el uso de goroutines al procesar múltiples tareas?
- ¿Cómo se puede medir el tiempo de ejecución de una tarea en Go?
- ¿Qué sucede si ejecutas el script varias veces respecto al orden de finalización de las rutinas?
- ¿Cuál es el propósito de la función
time.Sleep
en el ejemplo presentado? - Menciona una buena práctica al trabajar con concurrencia en Go.