Wie beendet man die Ausführung mehrerer Goroutines in Go richtig?
sobes.tech KI
Antwort von AI
Es gibt mehrere gängige Methoden:
-
Verwendung des Kontexts (Context): Empfohlener Ansatz zum Abbrechen von Operationen in einer Baumstruktur von Aufrufen. Der Kontext kann ein Abbruchsignal über einen Kanal übertragen.
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context, id int) { for { select { case <-ctx.Done(): fmt.Printf("Goroutine %d wird beendet: %v\n", id, ctx.Err()) return default: fmt.Printf("Goroutine %d arbeitet...\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() // Senden des Abbruchs-Signals time.Sleep(1 * time.Second) // Warten, bis die Goroutines beendet sind fmt.Println("Hauptprogramm beendet.") } -
Verwendung von Kanälen: Durch das Senden eines Signals (z.B. eine leere Struktur
struct{}) über einen speziellen Kanal kann eine Goroutine über die Beendigung informiert werden.package main import ( "fmt" "time" ) func worker(stopChan <-chan struct{}, id int) { for { select { case <-stopChan: fmt.Printf("Goroutine %d beendet über Kanal\n", id) return default: fmt.Printf("Goroutine %d arbeitet...\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) // Schließen des Kanals signalisiert das Ende time.Sleep(1 * time.Second) // Warten, bis die Goroutines beendet sind fmt.Println("Hauptprogramm beendet.") } -
Verwendung von
sync.WaitGroup(zum Warten auf Beendigung, nicht zum Signalisieren):WaitGroupwird verwendet, um auf das Ende einer Gruppe von Goroutines zu warten, bietet aber keinen Mechanismus zum Anhalten. Es wird oft in Kombination mit Kontexten oder Kanälen verwendet.package main import ( "context" "fmt" "sync" "time" ) func worker(ctx context.Context, wg *sync.WaitGroup, id int) { defer wg.Done() // Verringert den Zähler des WaitGroup bei Beendigung der Goroutine for { select { case <-ctx.Done(): fmt.Printf("Goroutine %d wird beendet\n", id) return default: fmt.Printf("Goroutine %d arbeitet...\n", id) time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) var wg sync.WaitGroup numWorkers := 2 wg.Add(numWorkers) // Initialisierung des WaitGroup for i := 1; i <= numWorkers; i++ { go worker(ctx, &wg, i) } time.Sleep(3 * time.Second) cancel() // Abbruchsignal wg.Wait() // Warten auf alle Goroutines fmt.Println("Hauptprogramm beendet.") }
Die Wahl der Methode hängt vom spezifischen Anwendungsfall und der Komplexität der Goroutine-Interaktionen ab. Der Kontext wird im Allgemeinen für Kaskadencancellationen bevorzugt.