Sobes.tech
Middle

Go-da bir neçə goroutine-un düzgün şəkildə necə tamamlanması?

sobes.tech Süni İntellekt

AI-dan cavab

Müxtəlif yaygın üsullar mövcuddur:

  • Kontextdən istifadə (Context): Ağac şəklində çağırışlar strukturunda əməliyyatların ləğvi üçün tövsiyə olunan yanaşmadır. Kontext ləğv siqnalını kanal vasitəsilə ötürə bilər.

    package main
    
    import (
        "context"
        "fmt"
        "time"
    )
    
    func worker(ctx context.Context, id int) {
        for {
            select {
            case <-ctx.Done():
                fmt.Printf("Goroutine %d tamamlanır: %v\n", id, ctx.Err())
                return
            default:
                fmt.Printf("Goroutine %d işləyir...\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() // Ləğv siqnalını göndəririk
    
        time.Sleep(1 * time.Second) // Gorutinaların tamamlanması üçün vaxt veririk
        fmt.Println("Əsas proqram tamamlandı.")
    }
    
  • Kanalardan istifadə: Xüsusi kanala (məsələn, boş struktur struct{}) siqnal göndərməklə, gorutinə tamamlanma ehtiyacını bildirə bilərsiniz.

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func worker(stopChan <-chan struct{}, id int) {
        for {
            select {
            case <-stopChan:
                fmt.Printf("Goroutine %d kanaldan tamamlanır\n", id)
                return
            default:
                fmt.Printf("Goroutine %d işləyir...\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) // Kanalın bağlanması tamamlanma siqnalını verir
    
        time.Sleep(1 * time.Second) // Gorutinaların tamamlanması üçün vaxt veririk
        fmt.Println("Əsas proqram tamamlandı.")
    }
    
  • sync.WaitGroup istifadə edərək (təkcə gözləmə üçün, siqnal vermək üçün deyil): WaitGroup gorutinaların tamamlanmasını gözləmək üçün istifadə olunur, lakin özü dayandırma mexanizmi təmin etmir. Onu tez-tez kontekst və ya kanallar ilə birlikdə istifadə edirlər.

    package main
    
    import (
        "context"
        "fmt"
        "sync"
        "time"
    )
    
    func worker(ctx context.Context, wg *sync.WaitGroup, id int) {
        defer wg.Done() // Gorutinal tamamlandıqda WaitGroup sayğacını azaldır
    
        for {
            select {
            case <-ctx.Done():
                fmt.Printf("Goroutine %d tamamlanır\n", id)
                return
            default:
                fmt.Printf("Goroutine %d işləyir...\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-i başlatırıq
    
        for i := 1; i <= numWorkers; i++ {
            go worker(ctx, &wg, i)
        }
    
        time.Sleep(3 * time.Second)
        cancel() // Ləğv siqnalı
    
        wg.Wait() // Bütün gorutinaların tamamlanmasını gözləyirik
        fmt.Println("Əsas proqram tamamlandı.")
    }
    

Seçim metodu konkret istifadə halına və gorutinlərin qarşılıqlı əlaqəsinin mürəkkəbliyinə bağlıdır. Context ümumiyyətlə kaskad ləğvlər üçün üstün tutulur.