Cum să închei corect execuția mai multor goroutines în Go?
sobes.tech AI
Răspuns de la AI
Există mai multe metode comune:
-
Utilizarea contextului (Context): Abordarea recomandată pentru anularea operațiilor într-o structură de tip arbore de apeluri. Contextul poate transmite un semnal de anulare printr-un canal.
package main import ( "context" "fmt" "time" ) func worker(ctx context.Context, id int) { for { select { case <-ctx.Done(): fmt.Printf("Goroutine %d se încheie: %v\n", id, ctx.Err()) return default: fmt.Printf("Goroutine %d lucrează...\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() // Trimite semnalul de anulare time.Sleep(1 * time.Second) // Așteaptă terminarea goroutine-urilor fmt.Println("Programul principal s-a încheiat.") } -
Utilizarea canalelor: Prin trimiterea unui semnal (de exemplu, o structură goală
struct{}) printr-un canal special, se poate informa goroutine-ul despre necesitatea de a se termina.package main import ( "fmt" "time" ) func worker(stopChan <-chan struct{}, id int) { for { select { case <-stopChan: fmt.Printf("Goroutine %d se încheie prin canal\n", id) return default: fmt.Printf("Goroutine %d lucrează...\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) // Închiderea canalului semnalează terminarea time.Sleep(1 * time.Second) // Așteaptă terminarea goroutine-urilor fmt.Println("Programul principal s-a încheiat.") } -
Utilizarea
sync.WaitGroup(pentru a aștepta finalizarea, nu pentru semnalizare):WaitGroupeste folosit pentru a aștepta finalizarea unui grup de goroutine-uri, dar nu oferă un mecanism de oprire. Este adesea combinat cu contexte sau canale.package main import ( "context" "fmt" "sync" "time" ) func worker(ctx context.Context, wg *sync.WaitGroup, id int) { defer wg.Done() // Scade contorul WaitGroup la finalizarea goroutine-ului for { select { case <-ctx.Done(): fmt.Printf("Goroutine %d se încheie\n", id) return default: fmt.Printf("Goroutine %d lucrează...\n", id) time.Sleep(1 * time.Second) } } } func main() { ctx, cancel := context.WithCancel(context.Background()) var wg sync.WaitGroup numWorkers := 2 wg.Add(numWorkers) // Inițializează WaitGroup for i := 1; i <= numWorkers; i++ { go worker(ctx, &wg, i) } time.Sleep(3 * time.Second) cancel() // Semnal de anulare wg.Wait() // Așteaptă finalizarea tuturor goroutine-urilor fmt.Println("Programul principal s-a încheiat.") }
Alegerea metodei depinde de cazul de utilizare specific și de complexitatea interacțiunilor dintre goroutine-uri. Contextul este în general preferat pentru anulări în cascadă.