Middle
Kaip teisingai užbaigti kelių goroutine vykdymą Go?
sobes.tech AI
Atsakymas iš AI
Yra keletas dažniausiai naudojamų būdų:
-
Konteksto naudojimas (Context): Rekomenduojamas būdas atšaukti operacijas medžio struktūroje. Kontekstą galima perduoti atšaukimo signalui per kanalą.
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context, id int) { for { select { case <-ctx.Done(): fmt.Printf("Gorutina %d baigsis: %v\n", id, ctx.Err()) return default: fmt.Printf("Gorutina %d dirba...\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() // Siųsti atšaukimo signalą time.Sleep(1 * time.Second) // Leisti gorutinoms užbaigti fmt.Println("Pagrindinė programa baigta.") } -
Kanalų naudojimas: Siunčiant signalą (pavyzdžiui, tuščią struktūrą
struct{}) per specialų kanalą, galima pranešti gorutinai apie būtinybę baigti.package main import ( "fmt" "time" ) func worker(stopChan <-chan struct{}, id int) { for { select { case <-stopChan: fmt.Printf("Gorutina %d baigsis per kanalą\n", id) return default: fmt.Printf("Gorutina %d dirba...\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) // Kanalas uždaromas, signalizuojant apie pabaigą time.Sleep(1 * time.Second) // Leisti gorutinoms užbaigti fmt.Println("Pagrindinė programa baigta.") } -
Naudojant
sync.WaitGroup(laukimo pabaigos, o ne signalo):WaitGroupnaudojamas laukti grupės gorutinų pabaigos, bet pats savaime nesuteikia sustabdymo mechanizmo. Jis dažnai naudojamas kartu su kontekstu ar kanalais.package main import ( "context" "fmt" "sync" "time" ) func worker(ctx context.Context, wg *sync.WaitGroup, id int) { defer wg.Done() // sumažina WaitGroup skaitiklį, kai gorutina baigiasi for { select { case <-ctx.Done(): fmt.Printf("Gorutina %d baigsis\n", id) return default: fmt.Printf("Gorutina %d dirba...\n", id) time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) var wg sync.WaitGroup numWorkers := 2 wg.Add(numWorkers) // nustatome WaitGroup for i := 1; i <= numWorkers; i++ { go worker(ctx, &wg, i) } time.Sleep(3 * time.Second) cancel() // atšaukimo signalas wg.Wait() // laukiame visų gorutinų pabaigos fmt.Println("Pagrindinė programa baigta.") }
Pasirinktas metodas priklauso nuo konkretaus naudojimo atvejo ir sudėtingumo gorutinų sąveikoje. Kontekstas paprastai yra pageidautinas kaskadiniam atšaukimui.