Come terminare correttamente l'esecuzione di più goroutine in Go?
sobes.tech AI
Risposta dell'AI
Esistono diversi metodi comuni:
-
Utilizzo del contesto (Context): Approccio raccomandato per annullare le operazioni in una struttura ad albero di chiamate. Il contesto può trasmettere un segnale di annullamento tramite un canale.
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context, id int) { for { select { case <-ctx.Done(): fmt.Printf("Goroutine %d si termina: %v\n", id, ctx.Err()) return default: fmt.Printf("Goroutine %d in esecuzione...\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() // Invia il segnale di annullamento time.Sleep(1 * time.Second) // Attende che le goroutine terminino fmt.Println("Programma principale terminato.") } -
Utilizzo dei canali: Inviando un segnale (ad esempio, una struttura vuota
struct{}) tramite un canale speciale, si può informare la goroutine che deve terminare.package main import ( "fmt" "time" ) func worker(stopChan <-chan struct{}, id int) { for { select { case <-stopChan: fmt.Printf("Goroutine %d si termina tramite canale\n", id) return default: fmt.Printf("Goroutine %d in esecuzione...\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) // La chiusura del canale indica che deve terminare time.Sleep(1 * time.Second) // Attende che le goroutine terminino fmt.Println("Programma principale terminato.") } -
Utilizzo di
sync.WaitGroup(per aspettare la fine, non per segnalare):WaitGroupviene usato per aspettare che un gruppo di goroutine finisca, ma non fornisce un meccanismo di arresto. Viene spesso combinato con contesti o canali.package main import ( "context" "fmt" "sync" "time" ) func worker(ctx context.Context, wg *sync.WaitGroup, id int) { defer wg.Done() // Diminuisce il contatore del WaitGroup al termine for { select { case <-ctx.Done(): fmt.Printf("Goroutine %d si termina\n", id) return default: fmt.Printf("Goroutine %d in esecuzione...\n", id) time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) var wg sync.WaitGroup numWorkers := 2 wg.Add(numWorkers) // Inizializza il WaitGroup for i := 1; i <= numWorkers; i++ { go worker(ctx, &wg, i) } time.Sleep(3 * time.Second) cancel() // Segnale di annullamento wg.Wait() // Attende che tutte le goroutine finiscano fmt.Println("Programma principale terminato.") }
La scelta del metodo dipende dal caso d'uso specifico e dalla complessità delle interazioni tra goroutine. In generale, il contesto è preferito per cancellazioni a cascata.