Oldingi bo'limda biz goroutine'lar va kanallarning asosiy mexanizmlari bilan tanishdik. Endi esa bir vaqtda ishlovchi goroutine'larni xavfsiz sinxronlash (synchronization) san'atini o'rganamiz!
sync.WaitGroup — bu bir nechta goroutine'lar to'liq tugagunga qadar dasturning asosiy oqimini to'xtatib turish uchun xizmat qiluvchi qulay hisoblagichdir.
U uchta asosiy metodga ega:
Add(delta int) — hisoblagichni delta miqdoriga oshiradi (yoki kamaytiradi).Done() — hisoblagichni 1 ga kamaytiradi (aslida bu Add(-1) ning o'zi).Wait() — hisoblagich 0 ga teng bo'lguncha joriy goroutine'ni bloklaydi.package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done() // ish tugagach hisoblagichni kamaytirish kafolati
fmt.Printf("Ishchi #%d ishni boshladi
", id)
time.Sleep(100 * time.Millisecond)
fmt.Printf("Ishchi #%d tugatdi
", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 3; i++ {
wg.Add(1) // goroutine boshlanishidan OLDIN chaqiriladi!
go worker(i, &wg)
}
wg.Wait() // barcha 3 ta ishchi tugashini kutadi
fmt.Println("Barcha ishchilar muvaffaqiyatli yakunlandi!")
}
sync.WaitGroup Go standart kutubxonasida qanday tuzilgan?
Uning ichida oddiy atomic hisoblagich va operatsion tizim semafori joylashgan:
Wait() chaqirib kutib turgan goroutine'lar soni turadi;Wait() chaqirgan barcha oqimlar bir vaqtda uyg'otiladi.[!CAUTION]
sync.WaitGroupobyektidan nusxa ko'chirib bo'lmaydi! Agar siz uni funksiyaga qiymat (sync.WaitGroup) sifatida uzatsangiz, yangi nusxa yaratiladi vamaindagi WaitGroup hech qachon kamaymaydi. Natijada deadlock yuz beradi!Har doim ko'rsatkich (
*sync.WaitGroup) orqali uzating:example.gofunc worker(wg *sync.WaitGroup) // TO'G'RI func worker(wg sync.WaitGroup) // XATO! Nusxa olinadi!
Eng yaxshi dasturlash amaliyoti shuki, ishchi funksiya WaitGroup haqida umuman bilmasligi kerak. Concurrency mantig'ini biznes mantig'iga aralashtirmang:
func doWork(id int) {
// toza biznes mantiq
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
doWork(id)
}(i)
}
wg.Wait()
}
Go 1.25+ da esa buni yanada qulayroq wg.Go(func() { doWork(id) }) orqali yozish mumkin.
wg.Wait() kutilayotgan paytda boshqa goroutinedan wg.Add(1) chaqirish qat'iyan man etiladi va bu panic keltirib chiqaradi. Barcha Add() amallari Wait() chaqirilgunga qadar yoki yangi kutish sikli boshlanishidan oldin to'liq bajarilgan bo'lishi shart.
3 ta faylni parallel yuklab, ularning hajmlarini hisoblovchi dastur tuzing. sync.WaitGroup orqali barcha yuklashlar tugashini kuting.
package main
import (
"fmt"
"sync"
"time"
)
func downloadFile(name string) int {
time.Sleep(50 * time.Millisecond)
return len(name) * 1024
}
func main() {
files := []string{"video.mp4", "music.mp3", "doc.pdf"}
var wg sync.WaitGroup
// TODO: Har bir fayl uchun alohida goroutine oching va WaitGroup orqali kuting
// wg.Wait()
fmt.Println("Barcha fayllar yuklandi!")
}
sync.WaitGroup goroutine'lar guruhining ishini yakunlashini kutish uchun eng optimal va tezkor vositadir.
Keyingi mavzu: Data races (Ma'lumotlar poygasi va sync.Mutex)!