Contenido del curso

Estructuras de Datos y Funciones

Goroutines y channels en Go

Resumen

La concurrencia en Go se resuelve con dos piezas que trabajan juntas: goroutines y channels. Si vienes de otros lenguajes con async/await, aquí descubrirás una sintaxis más simple para correr código en paralelo, sincronizar hilos y devolver resultados al flujo principal sin dolores de cabeza.

¿Qué es una goroutine en Go y cómo se ejecuta?

Una goroutine es una función que corre en un hilo asíncrono y devuelve el control al hilo principal cuando termina. La palabra reservada go antes de una llamada es todo lo que necesitas para lanzarla [01:02].

Para verlo en acción, dentro de una carpeta goroutine se crea un main.go que importa el paquete time y declara una función con un argumento from string que ejecuta un for tres veces con fmt.Println [02:00].

go func function(from string) { for i := 0; i < 3; i++ { fmt.Println(from, ":", i) } }

En modo directo, basta con function("directo") dentro de main y la salida aparece sin sorpresas. El cambio interesante ocurre al anteponer go:

go go function("goroutine")

¿Por qué mi goroutine no imprime nada? Porque el programa principal terminó antes de que el segundo hilo devolviera el resultado. La goroutine muere con el ciclo natural de la aplicación.

¿Cómo espero a que termine una goroutine?

Una solución rápida es usar time.Sleep para darle margen al hilo secundario [05:30]. También puedes declarar una goroutine anónima en línea con go func() { ... }() y ejecutarla una sola vez.

go go func(msg string) { fmt.Println(msg) }("goroutine anónima")

time.Sleep(time.Second) fmt.Println("Fin del programa")

El detalle simpático de la sintaxis de Go es que no existen async ni await: la palabra go se encarga de toda la orquestación. Aun así, depender de Sleep es frágil. Aquí entran los canales.

¿Qué son los channels en Go y para qué sirven?

Un channel es una estructura que permite a una goroutine enviar datos al hilo principal y, de paso, sincronizar la espera. Se declara con make y la palabra reservada chan seguida del tipo que va a transportar [07:45].

go mensajes := make(chan string)

go func() { mensajes <- "ping" }()

mensaje := <-mensajes fmt.Println(mensaje)

El operador <- cumple dos roles: enviar un valor al canal cuando aparece a la derecha de la variable, y recibirlo cuando aparece a la izquierda. La gran diferencia con el ejemplo de Sleep es que el canal espera por sí mismo a que la goroutine termine.

¿Para qué uso un channel en lugar de Sleep? Para que el hilo principal espere de forma natural el resultado de la goroutine sin temporizadores. El canal recibe el valor, lo entrega y se cierra cuando termina su ciclo de vida.

¿Puedo usar varios channels y goroutines a la vez?

Sí, y es donde la programación asíncrona se vuelve cómoda. Puedes declarar un segundo canal independiente del primero, lanzar otra goroutine y leer su mensaje sin que ambos se interfieran [10:20].

go go func() { mensajes <- "pong" }()

segundoMensaje := <-mensajes fmt.Println(segundoMensaje)

Cada canal vive su propio ciclo: se crea, transporta el valor, lo entrega y se libera. Esto te permite encadenar tareas concurrentes sin coordinar manualmente los tiempos.

¿Cuándo conviene usar goroutines y channels juntos?

La combinación brilla cuando necesitas procesamiento paralelo con sincronización: lecturas a APIs, trabajos en background, pipelines de datos o cualquier tarea donde el hilo principal dependa de un resultado externo.

Algunos casos prácticos donde la dupla aporta valor:

  • Ejecutar varias llamadas concurrentes y recibir cada respuesta por su propio canal.
  • Distribuir trabajo entre hilos del procesador para acelerar tareas pesadas.
  • Coordinar productores y consumidores sin escribir lógica de bloqueo manual.

La palabra reservada go, la función make aplicada a chan string y el operador <- son las tres piezas mínimas que necesitas memorizar. Con eso, tus aplicaciones en Go pueden trabajar con múltiples hilos, devolver información al flujo principal y mantener la sintaxis limpia.

¿Ya probaste portar una función bloqueante a una goroutine con channel? Cuenta en los comentarios qué caso de tu proyecto te gustaría resolver con concurrencia.