Sobes.tech
Middle

Hogyan zárjuk le helyesen több goroutine futását Go-ban?

sobes.tech MI

Válasz az MI-től

Néhány gyakori módszer létezik:

  • A kontextus használata: Ajánlott megközelítés a műveletek törlésére a fastruktúrában. A kontextus jelet küldhet a csatornán keresztül.

    package main
    
    import (
        "context"
        "fmt"
        "time"
    )
    
    func worker(ctx context.Context, id int) {
        for {
            select {
            case <-ctx.Done():
                fmt.Printf("Goroutine %d befejeződik: %v\n", id, ctx.Err())
                return
            default:
                fmt.Printf("Goroutine %d működik...\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() // A leállítási jel küldése
    
        time.Sleep(1 * time.Second) // Időt adunk a goroutine-oknak a befejezéshez
        fmt.Println("Fő program befejeződött.")
    }
    
  • Csatornák használata: Egy speciális csatornára (pl. üres struktúra struct{}) küldött jelzéssel jelezhető a goroutine-nak a befejezés szükségessége.

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func worker(stopChan <-chan struct{}, id int) {
        for {
            select {
            case <-stopChan:
                fmt.Printf("Goroutine %d a csatornán keresztül befejeződik\n", id)
                return
            default:
                fmt.Printf("Goroutine %d működik...\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) // A csatorna bezárása jelzi a befejezést
    
        time.Sleep(1 * time.Second) // Időt adunk a goroutine-oknak a befejezéshez
        fmt.Println("Fő program befejeződött.")
    }
    
  • sync.WaitGroup használata (csak várakozásra, nem jelzésre): A WaitGroup a gorutinek befejezésének várására szolgál, de önmagában nem nyújt leállítási mechanizmust. Gyakran kontextussal vagy csatornákkal együtt használják.

    package main
    
    import (
        "context"
        "fmt"
        "sync"
        "time"
    )
    
    func worker(ctx context.Context, wg *sync.WaitGroup, id int) {
        defer wg.Done() // Csökkenti a WaitGroup számlálót a gorutine befejezésekor
    
        for {
            select {
            case <-ctx.Done():
                fmt.Printf("Goroutine %d befejeződik\n", id)
                return
            default:
                fmt.Printf("Goroutine %d működik...\n", id)
                time.Sleep(1 * time.Second)
            }
        }
    }
    
    func main() {
        ctx, cancel := context.WithCancel(context.Background())
        var wg sync.WaitGroup
    
        numWorkers := 2
        wg.Add(numWorkers) // Inicializáljuk a WaitGroup-ot
    
        for i := 1; i <= numWorkers; i++ {
            go worker(ctx, &wg, i)
        }
    
        time.Sleep(3 * time.Second)
        cancel() // A leállítási jel
    
        wg.Wait() // Várjuk, hogy minden goroutine befejeződjön
        fmt.Println("Fő program befejeződött.")
    }
    

A módszer választása az adott felhasználási esettől és a goroutine-ok közötti összetettségtől függ. Általában a contextet preferálják a láncolt leállásokhoz.