Pense em um cenário onde você abra centenas ou talvez milhares de goroutines, porém que essa quantidade não seja fixa.
Utilizar um channel para controlar a quantidade de goroutines que já finalizaram a execução pode ser muito trabalhoso e em alguns casos até impossível já que, para um channel simples, teriamos que esperar a execução de cada goroutine antes de iniciar a próxima.
Se resolvemos usar um channel com buffer, teremos que especificar sua capacidade durante sua criação, o que também pode nos levar a criar um buffer muito pequeno, onde não conseguimos iniciar todas as goroutines que queremos de uma só vez.
Se você quiser saber mais sobre goroutines e channels antes de continuar, vou deixar aqui a lista com os 3 últimos posts onde abordamos esses assuntos:
- O que são e como funcionam as goroutines
- O que são e como utilizar channels
- Buffer de mensagens em channels
Bom, mas se gerir a execução das goroutines com channels pode ser trabalhoso ou em alguns casos até mesmo inviável, como podemos fazer?
Simples, utilizando um package nativo do Go que chama sync
. Para ser mais específico, uma struct chamada WaitGroup
. Essa struct contém 3 métodos para nos auxiliar nessa tarefa.
- Add: Incrementa o número de goroutines em execução;
- Done: Decrementa o número de goroutines em execução;
- Wait: Aguarda que o número de goroutines em execução chegue a zero.
Para facilitar o entendimento, vamos escrever um pequeno programa para executar um número aleatório de goroutines, entre 0 e 100, onde cada goroutine terá um Sleep entre 0s e 10s.
package main import ( "fmt" "math/rand" "sync" "time" ) func process(num int, wg *sync.WaitGroup) { fmt.Printf("iniciando goroutine num %d \n", num) time.Sleep(time.Second * time.Duration(rand.Intn(10))) fmt.Printf("finalizando goroutine num %d \n", num) wg.Done() } func main() { limit := rand.Intn(100) var wg sync.WaitGroup for i := 0; i <= limit; i++ { wg.Add(1) go process(i, &wg) } wg.Wait() fmt.Printf("%d goroutines foram finalizadas com sucesso! \n", limit) }
Nesse exemplo, podemos ver os 3 métodos sendo aplicados.
Antes de iniciar cada uma das goroutines, chamamos o métodos Add
para incrementar o número de goroutines em execução.
Passamos o ponteiro da struct para a função process
, onde ao final da sua execução chama o método Done
da struct WaitGroup
, para que seja feito um decremento no número de goroutines.
Fora do nosso for, chamamos o método Wait
, para que controle principal do programa fique bloqueado até que todas as goroutines sejam finalizadas.
Deixem suas dúvidas nos comentários.
Até a próxima!
[…] Aguardando a execução de múltiplas goroutines […]
[…] Aguardando a execução de múltiplas goroutines […]
[…] Aguardando execução de múltiplas goroutines […]
[…] Aguardando execução de múltiplas goroutines […]