¿Cómo finalizar correctamente la ejecución de varias goroutines en Go?
sobes.tech AI
Respuesta de la IA
Existen varias formas comunes:
-
Uso del contexto (Context): Enfoque recomendado para cancelar operaciones en estructuras de árbol de llamadas. El contexto puede transmitir una señal de cancelación a través de un canal.
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context, id int) { for { select { case <-ctx.Done(): fmt.Printf("Goroutine %d finaliza: %v\n", id, ctx.Err()) return default: fmt.Printf("Goroutine %d trabajando...\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() // Enviamos la señal de cancelación time.Sleep(1 * time.Second) // Damos tiempo a las goroutines para terminar fmt.Println("Programa principal finalizado.") } -
Uso de canales: Enviando una señal (por ejemplo, una estructura vacía
struct{}) a través de un canal especial, se puede informar a la goroutine que debe terminar.package main import ( "fmt" "time" ) func worker(stopChan <-chan struct{}, id int) { for { select { case <-stopChan: fmt.Printf("Goroutine %d finaliza por canal\n", id) return default: fmt.Printf("Goroutine %d trabajando...\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) // Cerrar el canal indica que debe terminar time.Sleep(1 * time.Second) // Damos tiempo a las goroutines para terminar fmt.Println("Programa principal finalizado.") } -
Usando
sync.WaitGroup(para esperar a que terminen, no para señalizar):WaitGroupse usa para esperar a que un grupo de goroutines termine, pero no proporciona un mecanismo de parada por sí mismo. A menudo se combina con contexto o canales.package main import ( "context" "fmt" "sync" "time" ) func worker(ctx context.Context, wg *sync.WaitGroup, id int) { defer wg.Done() // Disminuye el contador del WaitGroup al terminar for { select { case <-ctx.Done(): fmt.Printf("Goroutine %d finaliza\n", id) return default: fmt.Printf("Goroutine %d trabajando...\n", id) time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) var wg sync.WaitGroup numWorkers := 2 wg.Add(numWorkers) // Inicializamos el WaitGroup for i := 1; i <= numWorkers; i++ { go worker(ctx, &wg, i) } time.Sleep(3 * time.Second) cancel() // Señal de cancelación wg.Wait() // Esperamos a que terminen todas las goroutines fmt.Println("Programa principal finalizado.") }
La elección del método depende del caso de uso específico y de la complejidad de las interacciones entre goroutines. El contexto generalmente se prefiere para cancelaciones en cascada.