Goroutine'lar o'rtasida har doim ham katta hajmdagi ma'lumotlarni almashish shart emas. Ko'pincha ularga "Hodisa ro'y berdi!", "Initsializatsiya yakunlandi" yoki "Kesh yangilandi" degan qisqa signallar (signals) yetarli bo'ladi.
Agar siz bitta hodisa ro'y berganini kutayotgan bo'lsangiz, kanal yaratib unga qiymat yozish shart emas. Kanalni shunchaki close() qilish — eng mukammal signaldir:
Dasturda ba'zi amallar (masalan, ma'lumotlar bazasiga ulanish, konfiguratsiya faylini o'qish, og'ir modelni xotiraga yuklash) bir vaqtda ishlayotgan yuzlab goroutine'lardan qat'i nazar, roppa-rosa bir marta bajarilishi kerak.
Buning uchun Go'da sync.Once mavjud:
package main
import (
"fmt"
"sync"
)
var (
once sync.Once
config string
)
func loadConfig() {
once.Do(func() {
fmt.Println("Konfiguratsiya fayli yuklanmoqda... (faqat 1 marta!)")
config = "Production_Config_2026"
})
}
func main() {
var wg sync.WaitGroup
// 10 ta goroutine bir vaqtda konfiguratsiyani chaqiradi:
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
loadConfig()
}()
}
wg.Wait()
fmt.Println("Hamma goroutine'lar uchun config tayyor:", config)
}
Natija:
Konfiguratsiya fayli yuklanmoqda... (faqat 1 marta!)
Hamma goroutine'lar uchun config tayyor: Production_Config_2026
Go 1.21 versiyasida sync paketiga juda qulay yordamchi funksiyalar qo'shildi:
sync.OnceFunc(f) — f funksiyasini faqat bir marta bajaruvchi yangi funksiya qaytaradi.sync.OnceValue(f) — faqat bir marta hisoblab, keyingi barcha chaqiruvlarda o'sha hisoblangan qiymatni qaytaradi.sync.OnceValues(f) — qiymat va xatolik (val, err) qaytaruvchi funksiyalar uchun.// Faqat bir marta yuklab, keyin o'sha qiymatni qaytaradi:
var getDB = sync.OnceValue(func() string {
fmt.Println("DB connection ochilmoqda...")
return "DB_CONNECTION_OK"
})
func main() {
fmt.Println(getDB()) // ochiladi
fmt.Println(getDB()) // keshdan olinadi
}
Ko'p sonli parallel goroutine'lar doimiy ravishda yangi vaqtinchalik xotira (masalan, bytes.Buffer yoki byte slayslari) yaratsa, bu Garbage Collector'ga juda og'ir yuk bo'ladi.
sync.Pool vaqtinchalik obyektlarni qayta-qayta ishlatish (recycle) uchun xizmat qiladi:
var bufPool = sync.Pool{
New: func() any {
return new(bytes.Buffer)
},
}
func process() {
buf := bufPool.Get().(*bytes.Buffer) // pool'dan olamiz
buf.Reset()
defer bufPool.Put(buf) // ish tugagach pool'ga qaytaramiz
buf.WriteString("Tezkor xotira amali")
}
Bir nechta goroutine bir vaqtda murojaat qilganda sozlamalarni roppa-rosa 1 marta yuklovchi InitApp() funksiyasini sync.Once yordamida yozing.
package main
import (
"fmt"
"sync"
)
var (
appOnce sync.Once
ready bool
)
func InitApp() {
// TODO: appOnce.Do orqali ready = true qiling va "App initsializatsiya qilindi" deb chiqaring
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
InitApp()
}()
}
wg.Wait()
}
Signallar va sync.Once hamda sync.Pool Go dasturlarini ham xavfsiz, ham yuqori unumdor (high-performance) qilishda bebaho vositalardir.
Keyingi mavzu: Atomics (Atomik amallar va sync/atomic)!