WaitGroup

“Sincronizando Goroutines con Sync.WaitGroup en Go”

🚀 Introducción

Escribir software concurrente puede ser un desafío, pero el lenguaje de programación Go hace que sea más fácil. Consideremos el caso en el que necesitamos realizar varias tareas simultáneamente y tenemos que esperar a que todas ellas terminen antes de continuar. Aquí es donde entra en juego sync.WaitGroup. En este artículo, exploraremos cómo usar sync.WaitGroup en Go para sincronizar la ejecución de goroutines. Aprenderás cómo se utiliza sync.WaitGroup, cuándo utilizarlo y cómo evitar errores comunes.

📘 Fundamentos teóricos

sync.WaitGroup es una estructura simple de Go que espera a que una colección de goroutines finalice su ejecución antes de permitir que el programa continue. Es un mecanismo de conteo que incrementa su contador cuando agregas una goroutine y lo disminuye cuando una goroutine completa su ejecución. Cuando el contador llega a cero, desbloquea todas las goroutines en espera.

Se usa en situaciones donde necesitas ejecutar varias tareas de manera concurrente y necesitas que todas ellas terminen antes de proceder. Este es un patrón común en muchos programas - por ejemplo, si estás realizando múltiples operaciones independientes que pueden hacerse en paralelo, pero tu programa necesita esperar a que todas terminen antes de continuar.

var wg sync.WaitGroup

El WaitGroup debe ser una variable compartida a la que las goroutines tienen acceso, es decir, deben compartir el mismo WaitGroup. Puedes crearlo como una variable global o pasarlo a tus goroutines como argumento.

👨‍💻 Implementación Paso a Paso

El uso más común de sync.WaitGroup tiene tres etapas:

  1. Agregar al contador Usa wg.Add(<n>) para agregar n al contador:
wg.Add(1)
  1. Lanzar la goroutine Lanza tu funcion en una goroutine. En un defer, escribe wg.Done() para indicar que la goroutine ha terminado su ejecución:
go func() {
    defer wg.Done()
    // Código de tu goroutine
}()
  1. Esperar Finalmente, después de lanzar todas tus goroutines, usa wg.Wait para bloquear el programa hasta que todas las goroutines hayan completado su ejecución:
wg.Wait()

Aquí hay un ejemplo completo que ejecuta 3 goroutines:

package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()

    fmt.Printf("Worker %d starting\n", id)

    // Simulamos un trabajo costoso con un Sleep
    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 3; i++ {
        // Añade una goroutine al contador
        wg.Add(1)
        go worker(i, &wg)
    }

    // Espera a que todas las goroutines finalicen
    wg.Wait()
}

Al ejecutar este programa, verás que espera a que todas las goroutines terminen antes de que el programa finalice.

🏢 Casos de Uso Reales

sync.WaitGroup es útil en muchas situaciones. Aquí tienes dos ejemplos:

  1. Web scraping: Podrías querer extraer datos de varios sitios web de manera concurrente y esperar a que todos terminen antes de procesar los datos.
  2. APIs de fetch: Si tu aplicación depende de varias APIs, sync.WaitGroup puede ser útil para hacer las llamadas a la API en paralelo y esperar que todas respondan antes de continuar.

🚀 Patrones Avanzados

También puedes usar sync.WaitGroup junto con select y channels para crear patrones más complejos, por ejemplo, para implementar timeouts o para cancelar goroutines. Ten en cuenta que WaitGroup no tiene una cancelación incorporada, por lo que si necesitas cancelar goroutines, tendrás que usar context o canales.

👋 Conclusión y Recursos

En este artículo, has aprendido cómo usar sync.WaitGroup para sincronizar la ejecución de goroutines en Go. Aunque WaitGroup es una herramienta simple, puede ser poderosa cuando se usa correctamente. Como siempre, recuerda probar tu código y manejar los errores. Aquí está la documentación oficial de WaitGroup para más detalles. También podrías querer explorar otros mecanismos de synchronización proporcionados by Go, como Mutex y Channel.

¡Feliz codificación!