Jak poprawnie zakończyć wykonywanie wielu goroutine w Go?
sobes.tech AI
Odpowiedź od AI
Istnieje kilka powszechnych metod:
-
Użycie kontekstu (Context): Zalecane podejście do anulowania operacji w strukturze drzewa wywołań. Kontekst może przekazywać sygnał anulowania przez kanał.
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context, id int) { for { select { case <-ctx.Done(): fmt.Printf("Goroutine %d kończy się: %v\n", id, ctx.Err()) return default: fmt.Printf("Goroutine %d działa...\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() // Wysyłamy sygnał anulowania time.Sleep(1 * time.Second) // Czekamy na zakończenie goroutine fmt.Println("Główny program zakończony.") } -
Użycie kanałów: Wysyłając sygnał (np. pustą strukturę
struct{}) przez specjalny kanał, można poinformować goroutine o konieczności zakończenia.package main import ( "fmt" "time" ) func worker(stopChan <-chan struct{}, id int) { for { select { case <-stopChan: fmt.Printf("Goroutine %d kończy się przez kanał\n", id) return default: fmt.Printf("Goroutine %d działa...\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) // Zamknięcie kanału sygnalizuje zakończenie time.Sleep(1 * time.Second) // Czekamy na zakończenie goroutine fmt.Println("Główny program zakończony.") } -
Użycie
sync.WaitGroup(do oczekiwania na zakończenie, nie do sygnalizacji):WaitGroupsłuży do oczekiwania na zakończenie grupy goroutine, ale sam nie zapewnia mechanizmu zatrzymania. Często jest używany w połączeniu z kontekstami lub kanałami.package main import ( "context" "fmt" "sync" "time" ) func worker(ctx context.Context, wg *sync.WaitGroup, id int) { defer wg.Done() // Zmniejsza licznik WaitGroup po zakończeniu goroutine for { select { case <-ctx.Done(): fmt.Printf("Goroutine %d kończy się\n", id) return default: fmt.Printf("Goroutine %d działa...\n", id) time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) var wg sync.WaitGroup numWorkers := 2 wg.Add(numWorkers) // Inicjalizacja WaitGroup for i := 1; i <= numWorkers; i++ { go worker(ctx, &wg, i) } time.Sleep(3 * time.Second) cancel() // Sygnał anulowania wg.Wait() // Czekanie na zakończenie wszystkich goroutine fmt.Println("Główny program zakończony.") }
Wybór metody zależy od konkretnego przypadku użycia i złożoności interakcji goroutine. Kontekst jest zazwyczaj preferowany do kaskadowych anulowań.