Mutex
Bir nechta goroutine bitta o’zgaruvchini bir vaqtda o’zgartirsa, race condition kelib chiqadi: natija goroutine’lar qay tartibda ishlaganiga bog’lanib qoladi. counter++ aslida uch qadam (o’qi, oshir, yoz), shuning uchun ikki goroutine baravar oshirsa, bir oshirish “yo’qolib” ketishi mumkin.
sync.Mutex — qulf. Lock() bilan qulflaysiz, Unlock() bilan ochasiz; bir vaqtda qulfni faqat bitta goroutine ushlab turadi, qolganlar navbat kutadi:
package main
import (
"fmt"
"sync"
)
func main() {
counter := 0
var mu sync.Mutex
var wg sync.WaitGroup
for range 1000 {
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
counter++ // qulf ichida - xavfsiz
mu.Unlock()
}()
}
wg.Wait()
fmt.Println("counter:", counter)
}$ go run mutex.go
counter: 1000Qulfsiz bo’lsa, bu kod ko’pincha 1000 dan kam (masalan 973) chiqaradi. Lock/Unlock esa counter++ ni bo’linmas qilib qo’ydi.
Race detector va RWMutex
Bunday xatolarni Go’ning -race bayrog’i tutib oladi — go run -race mutex.go qaysi qatorda, qaysi goroutine’lar to’qnashayotganini aniq ko’rsatadi (WARNING: DATA RACE). Concurrent kodni har doim -race bilan sinang.
Ma’lumot ko’p o’qilib, kam yozilsa (kesh, konfiguratsiya), sync.RWMutex tezroq: RLock()/RUnlock() bir vaqtda ko’p o’quvchiga ruxsat beradi, Lock()/Unlock() esa faqat bitta yozuvchiga. Mutex’ni u himoyalayotgan ma’lumot bilan bitta struct’ga joylash — yaxshi uslub:
package main
import (
"fmt"
"sync"
)
type Config struct {
mu sync.RWMutex
data map[string]string
}
func (c *Config) Get(key string) string {
c.mu.RLock()
defer c.mu.RUnlock()
return c.data[key]
}
func (c *Config) Set(key, val string) {
c.mu.Lock()
defer c.mu.Unlock()
c.data[key] = val
}
func main() {
cfg := &Config{data: make(map[string]string)}
cfg.Set("host", "localhost")
var wg sync.WaitGroup
for range 3 {
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("host:", cfg.Get("host")) // bir vaqtda o'qish xavfsiz
}()
}
wg.Wait()
}$ go run rwmutex.go
host: localhost
host: localhost
host: localhostMap’lar o’z-o’zidan goroutine uchun xavfsiz emas (fatal error: concurrent map writes) — mutex ularni ham himoya qiladi.
Qisqasi: Mutex umumiy holatni race’dan himoyalaydi; o’qish ko’p bo’lsa RWMutex ishlating; xatolarni esa go run -race topib beradi.
Manba / batafsil: pkg.go.dev/sync#Mutex , Data Race Detector
Oddiy hisoblagichlar uchun mutex biroz og’irlik qiladi — avval sync.Once, so’ng undan tezroq sync/atomic ni ko’ramiz.