Como terminar corretamente a execução de várias goroutines em Go?
sobes.tech IA
Resposta da IA
Existem várias formas comuns:
-
Utilização do contexto (Context): Abordagem recomendada para cancelar operações em estruturas de árvore de chamadas. O contexto pode transmitir um sinal de cancelamento através de um canal.
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context, id int) { for { select { case <-ctx.Done(): fmt.Printf("Goroutine %d a terminar: %v\n", id, ctx.Err()) return default: fmt.Printf("Goroutine %d a trabalhar...\n", id) time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) go worker(ctx, 1) go worker(ctx, 2) time.Sleep(3 * time.Second) cancel() // Envia o sinal de cancelamento time.Sleep(1 * time.Second) // Dá tempo às goroutines para terminar fmt.Println("Programa principal terminado.") } -
Utilização de canais: Enviando um sinal (por exemplo, uma estrutura vazia
struct{}) através de um canal especial, pode-se informar à goroutine que ela deve terminar.package main import ( "fmt" "time" ) func worker(stopChan <-chan struct{}, id int) { for { select { case <-stopChan: fmt.Printf("Goroutine %d a terminar por canal\n", id) return default: fmt.Printf("Goroutine %d a trabalhar...\n", id) time.Sleep(1 * time.Second) } } } func main() { stopChan := make(chan struct{}) go worker(stopChan, 1) go worker(stopChan, 2) time.Sleep(3 * time.Second) close(stopChan) // Fechar o canal indica que deve terminar time.Sleep(1 * time.Second) // Dá tempo às goroutines para terminar fmt.Println("Programa principal terminado.") } -
Usando
sync.WaitGroup(para esperar que terminem, não para sinalizar):WaitGroupé usado para esperar que um grupo de goroutines termine, mas não fornece um mecanismo de parada por si só. É frequentemente combinado com contexto ou canais.package main import ( "context" "fmt" "sync" "time" ) func worker(ctx context.Context, wg *sync.WaitGroup, id int) { defer wg.Done() // Diminui o contador do WaitGroup ao terminar a goroutine for { select { case <-ctx.Done(): fmt.Printf("Goroutine %d a terminar\n", id) return default: fmt.Printf("Goroutine %d a trabalhar...\n", id) time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) var wg sync.WaitGroup numWorkers := 2 wg.Add(numWorkers) // Inicializa o WaitGroup for i := 1; i <= numWorkers; i++ { go worker(ctx, &wg, i) } time.Sleep(3 * time.Second) cancel() // Sinal de cancelamento wg.Wait() // Espera que todas as goroutines terminem fmt.Println("Programa principal terminado.") }
A escolha do método depende do caso de uso específico e da complexidade das interações entre goroutines. O contexto é geralmente preferido para cancelamentos em cascata.