Sobes.tech
Middle

Kā pareizi pabeigt vairākas goroutines izpildi Go valodā?

sobes.tech AI

Atbilde no AI

Ir vairākas bieži sastopamas metodes:

  • Konteksta izmantošana (Context): Ieteicams pieeja operāciju atcelšanai koka struktūrā. Kontekstu var nodot atcelšanas signālam caur kanālu.

    package main
    
    import (
        "context"
        "fmt"
        "time"
    )
    
    func worker(ctx context.Context, id int) {
        for {
            select {
            case <-ctx.Done():
                fmt.Printf("Gorutina %d beigsies: %v\n", id, ctx.Err())
                return
            default:
                fmt.Printf("Gorutina %d strādā...\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() // Nosūtīt atcelšanas signālu
    
        time.Sleep(1 * time.Second) // Dot laiku gorutinām pabeigties
        fmt.Println("Galvenā programma pabeigta.")
    }
    
  • Kanālu izmantošana: Nosūtot signālu (piemēram, tukšu struktūru struct{}) caur īpašu kanālu, var paziņot gorutīnai par nepieciešamību pabeigt.

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func worker(stopChan <-chan struct{}, id int) {
        for {
            select {
            case <-stopChan:
                fmt.Printf("Gorutina %d pabeigsies caur kanālu\n", id)
                return
            default:
                fmt.Printf("Gorutina %d strādā...\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) // Kanāla aizvēršana signālam par pabeigšanu
    
        time.Sleep(1 * time.Second) // Dot laiku gorutinām pabeigties
        fmt.Println("Galvenā programma pabeigta.")
    }
    
  • Ar sync.WaitGroup izmantošanu (gaidīšana pabeigšanai, nevis signāla sūtīšana): WaitGroup tiek izmantots, lai gaidītu grupas gorutīnu pabeigšanu, bet pats par sevi nesniedz apturēšanas mehānismu. To bieži izmanto kopā ar kontekstu vai kanāliem.

    package main
    
    import (
        "context"
        "fmt"
        "sync"
        "time"
    )
    
    func worker(ctx context.Context, wg *sync.WaitGroup, id int) {
        defer wg.Done() // samazina WaitGroup skaitītāju, kad gorutina pabeidzas
    
        for {
            select {
            case <-ctx.Done():
                fmt.Printf("Gorutina %d pabeigsies\n", id)
                return
            default:
                fmt.Printf("Gorutina %d strādā...\n", id)
                time.Sleep(1 * time.Second)
            }
        }
    }
    
    func main() {
        ctx, cancel := context.WithCancel(context.Background())
        var wg sync.WaitGroup
    
        numWorkers := 2
        wg.Add(numWorkers) // iestata WaitGroup
    
        for i := 1; i <= numWorkers; i++ {
            go worker(ctx, &wg, i)
        }
    
        time.Sleep(3 * time.Second)
        cancel() // atcelšanas signāls
    
        wg.Wait() // gaida visu gorutīnu pabeigšanu
        fmt.Println("Galvenā programma pabeigta.")
    }
    

Izvēlētais metode ir atkarīgs no konkrētā lietošanas gadījuma un gorutīnu mijiedarbības sarežģītības. Konteksts parasti ir ieteicams kaskadveida atcelšanai.