Sobes.tech
Middle

Ինչպես ճիշտ ավարտել մի քանի goroutine-ների գործարկումը Go-ում?

sobes.tech AI

Պատասխան AI-ից

Կան մի քանի տարածված մեթոդներ.

  • Կոնտեքստի (Context) օգտագործում: Առաջարկվող մոտեցում է գործողությունների չեղարկման համար ծառային կառուցվածքում: Կոնտեքստը կարող է փոխանցել չեղարկման նշան՝ միջոցով ալիքի:

    package main
    
    import (
        "context"
        "fmt"
        "time"
    )
    
    func worker(ctx context.Context, id int) {
        for {
            select {
            case <-ctx.Done():
                fmt.Printf("Գործողությունը %d ավարտվում է՝ %v\n", id, ctx.Err())
                return
            default:
                fmt.Printf("Գործողությունը %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() // Չեղարկման նշան ուղարկում
    
        time.Sleep(1 * time.Second) // ժամանակ տալ գործողություններին ավարտվելու
        fmt.Println("Հիմնական ծրագիրը ավարտվեց.")
    }
    
  • Ճանալի օգտագործում: Ուղարկելով նշան (օրինակ՝ դատարկ կառուցվածք struct{}) հատուկ ալիքի միջոցով, կարելի է տեղեկացնել goroutine-ին՝ անհրաժեշտության մասին՝ ավարտելու:

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func worker(stopChan <-chan struct{}, id int) {
        for {
            select {
            case <-stopChan:
                fmt.Printf("Goroutine %d ավարտվում է՝ ալիքի միջոցով\n", id)
                return
            default:
                fmt.Printf("Goroutine %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) // ալիքի փակումը նշում է ավարտը
    
        time.Sleep(1 * time.Second) // ժամանակ տալ goroutine-ներին ավարտելու
        fmt.Println("Հիմնական ծրագիրը ավարտվեց.")
    }
    
  • sync.WaitGroup օգտագործում (սպասելու համար, ոչ նշան տալու համար): WaitGroup-ը օգտագործվում է խմբի goroutine-ների ավարտին սպասելու համար, բայց ինքնին չի ապահովում կանգնեցման մեխանիզմ: Այն հաճախ օգտագործվում է կոնտեքստի կամ ալիքների հետ համատեղ:

    package main
    
    import (
        "context"
        "fmt"
        "sync"
        "time"
    )
    
    func worker(ctx context.Context, wg *sync.WaitGroup, id int) {
        defer wg.Done() // նվազեցնում է `WaitGroup`-ի հաշվիչը ավարտին հասնելիս
    
        for {
            select {
            case <-ctx.Done():
                fmt.Printf("Goroutine %d ավարտվում է\n", id)
                return
            default:
                fmt.Printf("Goroutine %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) // սկզբնականացման `WaitGroup`
    
        for i := 1; i <= numWorkers; i++ {
            go worker(ctx, &wg, i)
        }
    
        time.Sleep(3 * time.Second)
        cancel() // չեղարկման նշան
    
        wg.Wait() // սպասում է բոլոր goroutine-ների ավարտին
        fmt.Println("Հիմնական ծրագիրը ավարտվեց.")
    }
    

Ընտրությունը կախված է կոնկրետ օգտագործման դեպքից և goroutine-ների փոխազդեցության բարդությունից: Կոնտեքստը սովորաբար նախընտրելի է կասկադային չեղարկումների համար: