Sobes.tech
Middle

Hoe beëindig je correct de uitvoering van meerdere goroutines in Go?

sobes.tech AI

Antwoord van AI

Er zijn verschillende gangbare methoden:

  • Gebruik van de context (Context): Aanbevolen aanpak voor het annuleren van operaties in een boomstructuur van oproepen. De context kan een annuleringssignaal via een kanaal doorgeven.

    package main
    
    import (
        "context"
        "fmt"
        "time"
    )
    
    func worker(ctx context.Context, id int) {
        for {
            select {
            case <-ctx.Done():
                fmt.Printf("Goroutine %d wordt beëindigd: %v\n", id, ctx.Err())
                return
            default:
                fmt.Printf("Goroutine %d werkt...\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() // Verstuur het annuleringssignaal
    
        time.Sleep(1 * time.Second) // Wacht tot de goroutines beëindigd zijn
        fmt.Println("Hoofdschema beëindigd.")
    }
    
  • Gebruik van kanalen: Door een signaal (bijvoorbeeld een lege structuur struct{}) via een speciaal kanaal te sturen, kan een goroutine geïnformeerd worden dat deze moet stoppen.

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func worker(stopChan <-chan struct{}, id int) {
        for {
            select {
            case <-stopChan:
                fmt.Printf("Goroutine %d wordt beëindigd via kanaal\n", id)
                return
            default:
                fmt.Printf("Goroutine %d werkt...\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) // Het sluiten van het kanaal signaleert beëindiging
    
        time.Sleep(1 * time.Second) // Wacht tot de goroutines beëindigd zijn
        fmt.Println("Hoofdschema beëindigd.")
    }
    
  • Gebruik van sync.WaitGroup (om te wachten tot het eind, niet om te signaleren): WaitGroup wordt gebruikt om te wachten tot een groep goroutines klaar is, maar biedt geen mechanisme om te stoppen. Het wordt vaak gecombineerd met contexten of kanalen.

    package main
    
    import (
        "context"
        "fmt"
        "sync"
        "time"
    

)

func worker(ctx context.Context, wg *sync.WaitGroup, id int) {
    defer wg.Done() // Verlaagt de teller van de WaitGroup bij voltooiing

    for {
        select {
        case <-ctx.Done():
            fmt.Printf("Goroutine %d wordt beëindigd\n", id)
            return
        default:
            fmt.Printf("Goroutine %d werkt...\n", id)
            time.Sleep(1 * time.Second)
        }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    var wg sync.WaitGroup

    numWorkers := 2
    wg.Add(numWorkers) // Initialiseer de WaitGroup

    for i := 1; i <= numWorkers; i++ {
        go worker(ctx, &wg, i)
    }

    time.Sleep(3 * time.Second)
    cancel() // Signaal om te annuleren

    wg.Wait() // Wacht tot alle goroutines klaar zijn
    fmt.Println("Hoofdschema beëindigd.")
}
```

De keuze van de methode hangt af van de specifieke use case en de complexiteit van de interacties tussen goroutines. Context wordt over het algemeen geprefereerd voor cascade-onderbrekingen.