Concurrencia en Golang III
Tags: api, go, programming, c
Published: 2022-12-07 22:04:50

|1. | [Modelo de concurrencia](https://dev.to/tomaslingotti/concurrencia-en-go-i-5aao)
|2. | [Goroutines y canales](https://dev.to/tomaslingotti/concurrencia-en-golang-ii-484m)
|3. | **Wait Groups**
|4. | [Select y Worker pools](https://dev.to/tomaslingotti/concurrencia-en-golang-iv-5584)
|5. | [ErrGroup (error groups)](https://dev.to/tomaslingotti/concurrencia-en-go-v-4gif)

## 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.

```go
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()
}
```

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:

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

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á](https://github.com/sponsors/tomiok)!