WaitGroup
Bir nechta goroutine tugashini time.Sleep bilan taxmin qilish yaramaydi. To’g’ri asbob - sync.WaitGroup, oddiy hisoblagich:
wg.Add(n)- hisoblagichganqo’shadi (nechta goroutine kutamiz).wg.Done()- bittaga kamaytiradi (goroutine “tugadim” deydi).wg.Wait()- hisoblagich nolga tushguncha bloklaydi.
package main
import (
"fmt"
"sync"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // funksiya qanday tugasa ham chaqiriladi
fmt.Printf("ishchi %d tugatdi\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1) // goroutine dan OLDIN
go worker(i, &wg)
}
wg.Wait()
fmt.Println("barcha ishchilar tugadi")
}$ go run waitgroup.go
ishchi 3 tugatdi
ishchi 1 tugatdi
ishchi 2 tugatdi
barcha ishchilar tugadiIshchilar tartibi har xil, lekin barcha ishchilar tugadi doim oxirida. Uch qoida: WaitGroup ni pointer (*sync.WaitGroup) qilib uzating (nusxa deadlock beradi), defer wg.Done() ishlating, Add ni goroutine dan oldin chaqiring.
Loop o’zgaruvchisini “ushlab qolish” tuzog’i
Go 1.22 gacha for ichida closure loop o’zgaruvchisini ishlatganda barcha goroutine lar bitta va o’sha o’zgaruvchini baham ko’rar, ko’pincha oxirgi qiymatni ko’rardi:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("i =", i)
}()
}
wg.Wait()
}$ go run loopvar.go
i = 3
i = 1
i = 2Go 1.22 dan har iteratsiya i ning yangi nusxasini oladi, shuning uchun bu kod endi to’g’ri (1, 2, 3 - tartib aralash). Go 1.21 va eski versiyalarda esa aynan shu kod ko’pincha i = 3 ni uch marta chop etardi. Eski kodni barcha versiyada to’g’ri yozish uchun qiymatni parametr qilib uzating: go func(n int){ ... }(i).
Qisqasi: Add/Done/Wait bilan goroutine larni kuting; Go 1.22 loopvar tuzatuvidan keyin loop o’zgaruvchisi har iteratsiyada yangi.
Manba / batafsil: pkg.go.dev/sync#WaitGroup , go.dev/blog/loopvar-preview
WaitGroup goroutine larni kutadi, lekin umumiy o’zgaruvchini himoyalamaydi - buning uchun sync.Mutex keyingi darsda.