69 / 73

Signaling

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.

Bob mundarijasi:


Signallar va bir martalik obuna

Agar siz bitta hodisa ro'y berganini kutayotgan bo'lsangiz, kanal yaratib unga qiymat yozish shart emas. Kanalni shunchaki close() qilish — eng mukammal signaldir:

  • Yopilgan kanaldan o'qish hech qachon bloklanmaydi va darhol nol qiymat qaytaradi;
  • Uni bir vaqtda xohlagancha goroutine kutib turishi mumkin.

Faqat bir marta ishlatish: sync.Once

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:

example.go
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:

text
Konfiguratsiya fayli yuklanmoqda... (faqat 1 marta!)
Hamma goroutine'lar uchun config tayyor: Production_Config_2026

Go 1.21+ Once-funksiyalari

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.
example.go
// 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
}

Xotirani tejash: sync.Pool

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:

example.go
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")
}

Topshiriq: sync.Once orqali xavfsiz sozlamalarni yuklash

Bir nechta goroutine bir vaqtda murojaat qilganda sozlamalarni roppa-rosa 1 marta yuklovchi InitApp() funksiyasini sync.Once yordamida yozing.

example.go
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()
}

Xulosa va keyingi qadamlar

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)!