DEV Community

Cover image for Concurrencia en Golang III
Tomas Francisco Lingotti
Tomas Francisco Lingotti

Posted on • Edited on

Concurrencia en Golang III

|1. | Modelo de concurrencia
|2. | Goroutines y canales
|3. | Wait Groups
|4. | Select y Worker pools
|5. | ErrGroup (error groups)

WaitGroups, que son y para qué sirven?

Siempre me gusta comenzar los temas respondiendo estas dos preguntas, porque nos da un panorama general de que problemas nos ayuda a resolver y cuándo es mejor usar ésta tecnología.

Los WG son estructuras que nos permiten "alojar" rutinas (la cantidad que querramos) y -lo mas importante- poder esperar a que todas terminen. En resumen, es un mecanismo de sincronización de go-rutinas, donde podemos ejecutar todas las que sean necesarias y quedar a la espera a que la totalidad terminen.
Es muy útil cuando queremos paralelizar un trabajo pero tratándolo como una unidad, que una vez terminada, sigue el flujo normal de trabajo.

Para terminar la introducción, la struct de WaitGroup se encuentra en el paquete sync de la stdlib, no tenemos que importar nada.

Como se usan los wait groups?

Tenemos 3 métodos básicos que nos van a dar soporte a nuestra operación.

Vamos a mejorar el ejemplo que dimos en el post anterior, donde usábamos (mal) la función time.Sleep() para esperar que la goroutine termine. Ahora vamos a mostrar una forma correcta de hacer el mismo trabajo.

package main

import (
    "fmt"
    "sync"
)

var wg sync.WaitGroup

func main() {
    wg.Add(1)
    go doWork(&wg)
    wg.Wait()
    fmt.Println("exiting...")
}

func doWork(wg *sync.WaitGroup) {
    fmt.Println("hello world")
    wg.Done()
}
Enter fullscreen mode Exit fullscreen mode

Esta si es la forma correcta de ejecutar un hello world en una goroutine. Vamos paso a paso:

  • definimos la variable wg que es de tipo sync.WaitGroup, yo aconsejo declararla así para mejor lectura, aunque wg := sync.WaitGroup{} también es correcto.

  • Dentro de main, donde lanzamos la rutina con el prefijo go (eso ya lo sabemos hacer), tenemos el método Add(), que tiene como parámetro de entrada un entero (se llama delta) y ahi le vamos a especificar con una precisión absoluta, cuantas goroutines se va a agregar a la lista de espera, la misma cantidad que se agreguen, van a ser las que terminen.
    En caso de ejecutar muchas goroutines o que se lancen dentro de un bucle, en lugar de poner el numero exacto podemos igualmente siempre agregar 1; Por ejemplo:

// ...
for i, _ := range ints {
    wg.Add(1)
    go doWork(&wg, i)
}
// ...
Enter fullscreen mode Exit fullscreen mode

Entonces, independientemente de la cantidad de ints vamos a agregar siempre de a una.
Acá otra recomendación para Add(), y es que admite números negativos, para decrementar la cantidad en espera, yo NO lo recomiendo usar a menos que sea un caso de uso exacto, puede llevar a problemas cuyas soluciones sean muy difíciles de encontrar.

  • Vemos también que a la función doWork, le pasamos la dirección de memoria de la estructura wg. Esto es importante porque estrictamente queremos esa referencia y no modificar una copia, suele ser un error normal cuando empezamos a usar esta sincronización, este bien atentos a recibir punteros y pasar direcciones de memoria con '&'.
    En doWork, invocamos a Done(), puede usarse con la palabra reservada 'defer' para que no repetirnos en caso de tener varias salidas o return y como su nombre lo indica, Done() lo debemos llamar cuando el trabajo está terminado. Internamente va a descontar en una unidad a la lista de espera y marcar esa tarea como finalizada.

  • Por último, el método Wait(), que va a detener el control del programa hasta que el contador de la lista de espera de 0( cero) y todas las rutinas hayan ejecutado la función Done().

Conclusiones

Los WG son un mecanismo excelente para garantizar completitud de N cantidad de goroutines, fácil de implementar y probar, ademas de que la encontramos en la stdlib, como la gran mayoría de cosas que vamos a ver en estos artículos.

Ya saben, si quieren sponsorearme, pueden hacerlo acá!

Top comments (2)

Collapse
 
ladivaloper profile image
Camila Lenis

Estoy amando esta serie de blogs. Gracias Tomas 🤓 muy valioso

Collapse
 
tomaslingotti profile image
Tomas Francisco Lingotti

Muchas gracias Camila, cualquier otro tema que creas interesante podes dejarlos en comentarios.