Skip to content

WaitGroup — Goroutine'larni boshqarish

Bundan avvalgi mavzuda biz goroutine tushunchasi bilan tanishgan edik. Ushbu mavzuga o'tishdan oldin o'sha bilimlarni qisqacha yodga olib o'tish foydali bo'ladi.

Info

Go dasturlash tilida dastur salomilishi main() funksiyasidan boshlanadi. Agar main() funksiyasi o'z ishini yakunlasa, butun dastur ham to'xtaydi. Bu holatda, Go tilida asosiy goroutine deb aynan main() funksiyasi qaraladi. Yangi yaratilgan goroutinelar esa ana shu asosiy goroutine ustida ishlaydi.

Goroutine'lar bilan ishlaganda, ularning salomilishi tugashini kutish muhim bo'ladi. Avvalgi misolda biz bu muammoni vaqtinchalik time.Sleep(time.Second) funksiyasi orqali hal qilgan edik. Biroq bu usul samarali emas, chunki u aniq va ishonchli boshqaruvni ta'minlamaydi.

Go ishlab chiquvchilari ushbu muammoni hal qilish uchun maxsus mexanizm sync.WaitGroup paketini qo'shishgan. WaitGroup yordamida bir nechta goroutinelarni sinxronlashtirish va ularning boshqarish mumkin.

sync.WaitGroup'ning asosiy metodlari

  • Add(n int) - ishga tushirilayotgan goroutine'lar sonini belgilaydi.
  • Done() - bitta goroutine ishini tugatganini bildiradi.
  • Wait() - barcha qo'shilgan goroutine'lar Done() bo'lishini kutadi.

Misol

package main

import (
    "fmt"
    "sync"
)

func salom(id int, wg *sync.WaitGroup) {
    defer wg.Done() // Tugaganini bildiramiz
    fmt.Println("Salom!", id)
}

func main() {
    var wg sync.WaitGroup

    for i := 1; i <= 5; i++ {
        wg.Add(1) // Har bir goroutine uchun 1 qo'shamiz
        go salom(i, &wg)
    }

    wg.Wait() // Barchasini tugashini kutamiz
    fmt.Println("Barcha goroutine yakunlandi.")
}

Natija:

Salom! 5
Salom! 2
Salom! 1
Salom! 3
Salom! 4
Barcha goroutine yakunlandi.

Kodni ishlatib ko'ring va taxlil qiling, sonlarni o'zgartirib yana ham chuqurroq kiring.

Yuqoridagi kodni ishlatib ko'rib wg.Add(1) qiymatini wg.Add(n)(n - ixtiyoriy son) ga o'zgartirsak nima bo'ladi degan savol paydo bo'lgan bo'lishi mumkin, bu savolga quyida javob berib ketamiz:

Agar siz wg.Add(2) deb yozsangiz, bu WaitGroupga 2 ta goroutine tugashini kutish kerakligini bildiradi. Shunday bo'lsa, siz ikki marta wg.Done() metodini chaqirishingiz shart bo'ladi. Agar faqat bitta wg.Done() chaqirilsa Wait() abadiy kutishda qoladi (deadlock yuz berishi mumkin). Xulosa qilib aytganda wg.Add(n) ni qiymati n nechi bo'lsa shuncha marta Done() chaqirilishi shart.

Misol:

package main

import (
    "fmt"
    "sync"
)

func salom(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Println("Salom", id)
}

func main() {
    var wg sync.WaitGroup
    wg.Add(2) // 2 ta goroutine kutamiz

    go salom(1, &wg)
    go salom(2, &wg) // 2 ta goroutine chaqirildi

    wg.Wait()
    fmt.Println("Barchasi tugadi")
}

Natija:

Salom 2
Salom 1
Barchasi tugadi

deadlock holatiga misol:

package main

import (
    "fmt"
    "sync"
)

func salom(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Println("Salom", id)
}

func main() {
    var wg sync.WaitGroup
    wg.Add(4) // 4 ta goroutine kutamiz

    go salom(1, &wg)
    go salom(2, &wg) // 2 ta goroutine chaqirildi

    wg.Wait()
    fmt.Println("Barchasi tugadi")
}

Natija:

Salom 2
Salom 1
fatal error: all goroutines are asleep - deadlock!

WaitGroupning nozik jihatlari

  • Add() har doim goroutine ishga tushishidan oldin chaqirilishi kerak.
  • Done() ni unutib qo'yish mumkin — defer bilan ishlatish tavsiya etiladi.
  • WaitGroup qayta ishlatilmaydi, ya'ni Wait() dan keyin yana Add() chaqirish noto'g'ri hisoblanadi.

Xulosa

WaitGroup Go dasturlash tilida parallel ishlovchi jarayonlarni boshqarishni sodda, lekin kuchli vositasi.