Sobes.tech
Middle

Go'da bir nechta goroutine'larni qanday to'g'ri yakunlash mumkin?

sobes.tech AI

AIdan javob

Odatda keng tarqalgan usullar mavjud:

  • Kontekstdan foydalanish (Context): Chaqiriqlar daraxti tuzilmasida operatsiyalarni bekor qilish uchun tavsiya etilgan yondashuv. Kontekst kanal orqali bekor qilish signalini uzatishi mumkin.

    package main
    
    import (
        "context"
        "fmt"
        "time"
    )
    
    func worker(ctx context.Context, id int) {
        for {
            select {
            case <-ctx.Done():
                fmt.Printf("Goroutine %d yakunlanmoqda: %v\n", id, ctx.Err())
                return
            default:
                fmt.Printf("Goroutine %d ishlamoqda...\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() // Bekor qilish signalini yuborish
    
        time.Sleep(1 * time.Second) // Goroutine'lar tugashini kutish
        fmt.Println("Asosiy dastur yakunlandi.")
    }
    
  • Kanalardan foydalanish: Maxsus kanal orqali (masalan, bo'sh struktura struct{}) signal yuborish orqali goroutine'ga yakunlash kerakligini bildirish mumkin.

    package main
    
    import (
        "fmt"
        "time"
    )
    
    func worker(stopChan <-chan struct{}, id int) {
        for {
            select {
            case <-stopChan:
                fmt.Printf("Goroutine %d kanal orqali yakunlanmoqda\n", id)
                return
            default:
                fmt.Printf("Goroutine %d ishlamoqda...\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) // Kanalni yopish yakunlash signalini beradi
    
        time.Sleep(1 * time.Second) // Goroutine'lar tugashini kutish
        fmt.Println("Asosiy dastur yakunlandi.")
    }
    
  • sync.WaitGroup dan foydalanish (tugash uchun kutish, signal berish uchun emas): WaitGroup bir guruh goroutine'larning tugashini kutish uchun ishlatiladi, lekin o'z-o'zidan to'xtash mexanizmi taqdim etmaydi. Ko'pincha kontekstlar yoki kanallar bilan birga ishlatiladi.

    package main
    
    import (
        "context"
        "fmt"
        "sync"
        "time"
    )
    
    func worker(ctx context.Context, wg *sync.WaitGroup, id int) {
        defer wg.Done() // Goroutine tugagach, hisoblagichni kamaytiradi
    
        for {
            select {
            case <-ctx.Done():
                fmt.Printf("Goroutine %d yakunlanmoqda\n", id)
                return
            default:
                fmt.Printf("Goroutine %d ishlamoqda...\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 ni boshlash
    
        for i := 1; i <= numWorkers; i++ {
            go worker(ctx, &wg, i)
        }
    
        time.Sleep(3 * time.Second)
        cancel() // Bekor qilish signali
    
        wg.Wait() // Barcha goroutine'larning tugashini kutish
        fmt.Println("Asosiy dastur yakunlandi.")
    }
    

Usul tanlovi aniq foydalanish holatiga va goroutine'lar o'rtasidagi murakkablikka bog'liq. Kontekst odatda ketma-ket bekor qilishlar uchun afzalroq.