Sobes.tech
Middle

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): WaitGroup este 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ă.