Programación asíncrona en Go: guía completa para desarrolladores

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Programación asíncrona en Go: conceptos básicos y práctica



Go (Golang) fue diseñado desde el principio como un lenguaje para trabajar eficientemente con tareas concurrentes. A diferencia de muchos otros lenguajes, donde la asincronía se logra mediante callbacks o bibliotecas complejas, Go ofrece herramientas simples y elegantes: goroutines, canales y el operador select. En esta guía, analizaremos cómo funciona la programación asíncrona en Go y mostraremos ejemplos prácticos.



1. Goroutines: hilos de ejecución ligeros



Una goroutine es un hilo de ejecución ligero, gestionado por el runtime de Go. A diferencia de los hilos del sistema, las goroutines tienen una sobrecarga mínima. Puedes ejecutar miles de goroutines sin un impacto significativo en el rendimiento.



Para iniciar una goroutine se utiliza la palabra clave go antes de la llamada a la función:



package main

import ( "fmt" "time")

func printMessage(msg string) { for i := 0; i < 5; i++ { fmt.Println(msg) time.Sleep(100 * time.Millisecond) }}

func main() { // Inicio de goroutine go printMessage("Salida asíncrona") // El hilo principal continúa su trabajo printMessage("Salida síncrona") // Damos tiempo a la goroutine para que termine time.Sleep(1 * time.Second)}


Es importante recordar: la función principal main también es una goroutine. Si esta finaliza, todas las demás goroutines serán destruidas. Por eso, a menudo se utiliza sync.WaitGroup o canales para la sincronización.



2. Canales: transferencia segura de datos entre goroutines



Los canales son "tuberías" tipadas a través de las cuales las goroutines pueden intercambiar datos. El principio fundamental: "No se comuniquen a través de la memoria compartida; compartan la memoria a través de la comunicación".



Creación y uso de un canal:



package main

import "fmt"

func main() { // Creamos un canal no buferizado para int ch := make(chan int) // Iniciamos una goroutine que envía datos go func() { for i := 0; i < 5; i++ { ch <- i * 2 } close(ch) // Cerramos el canal después de enviar todos los datos }() // Leemos datos del canal for value := range ch { fmt.Println("Recibido:", value) }}


Canales buferizados permiten enviar datos sin necesidad de recibirlos inmediatamente:



ch := make(chan string, 3) // Buffer para 3 elementosch <- "A"ch <- "B"ch <- "C"// Ahora se puede leerfmt.Println(<-ch) // Afmt.Println(<-ch) // B


3. Operador select: trabajo con múltiples canales



El operador select permite a una goroutine esperar simultáneamente varias operaciones de canal. Se bloquea hasta que uno de sus casos esté listo para ejecutarse.



Ejemplo con timeout y manejo de múltiples fuentes de datos:



package main

import ( "fmt" "time")

func worker(id int, ch chan string) { time.Sleep(time.Duration(id) * time.Second) ch <- fmt.Sprintf("Resultado del worker %d", id)}

func main() { ch1 := make(chan string) ch2 := make(chan string) go worker(2, ch1) // Finaliza en 2 seg go worker(3, ch2) // Finaliza en 3 seg select { case msg1 := <-ch1: fmt.Println("Primer canal:", msg1) case msg2 := <-ch2: fmt.Println("Segundo canal:", msg2) case <-time.After(1 * time.Second): fmt.Println("¡Timeout! Ningún worker llegó a tiempo") } // Cerramos los canales para evitar fugas close(ch1) close(ch2)}


El operador select es ideal para implementar timeouts, operaciones no bloqueantes y el patrón "fan-in" (combinar varios canales en uno).



4. Patrones de concurrencia en Go



Considera

Blogs

Book Recommendations