Mutex'dan yengilroq alternativa
Oldingi darsda oddiy hisoblagichni himoya qilish uchun sync.Mutex ishlatgan edik. Lekin faqat bitta oddiy son ustida (qo'shish, ayirish, o'qish, yozish) amal bajarilayotgan bo'lsa, to'liq Mutex bilan qulflash biroz "og'ir" bo'lishi mumkin. Bunday holatlar uchun sync/atomic paketi ancha yengilroq va tezroq yechim taklif etadi.
import "sync/atomic"
Atomik amal — bu bo'linmaydigan, "bir zumda" bajariladigan amal: boshqa hech qanday goroutine uni yarim yo'lda "ushlab qolib", oraliq holatni ko'ra olmaydi.
atomic.AddInt64()package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var counter int64
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
// counter++ o'rniga, atomik qo'shish
atomic.AddInt64(&counter, 1)
}()
}
wg.Wait()
fmt.Println(atomic.LoadInt64(&counter))
}
Natija (endi Mutexsiz ham har doim to'g'ri):
1000
atomic.AddInt64(&counter, 1) — counter++ning oddiy Mutex.Lock()/Unlock() yozishga qaraganda qisqaroq va tezroq, bo'linmas muqobili. atomic.LoadInt64() esa qiymatni xavfsiz o'qish uchun ishlatiladi (oddiy counterni to'g'ridan-to'g'ri o'qish, hatto faqat o'qish bo'lsa ham, texnik jihatdan "poyga" hisoblanishi mumkin).
atomic.Int64Go 1.19 versiyasidan boshlab, sync/atomic qulayroq, tur xavfsizligi yaxshiroq ta'minlangan API ham taqdim etadi:
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var counter atomic.Int64 // maxsus tur, oddiy int64 emas
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
counter.Add(1)
}()
}
wg.Wait()
fmt.Println(counter.Load())
}
Natija:
1000
[!NOTE] Yangi loyihalarda
atomic.Int64,atomic.Int32,atomic.Bool,atomic.Valuekabi turdosh (typed) API'ni afzal ko'ring — u eskiatomic.AddInt64(&x, ...)uslubiga qaraganda xato qilish ehtimolini kamaytiradi (masalan, oddiy o'zgaruvchini tasodifanatomicfunksiyasisiz o'zgartirib qo'yish kabi xatolar bu yangi API bilan deyarli imkonsiz).
atomic va Mutex orasida qachon qaysi birini tanlash kerak?| Holat | Tavsiya |
|---|---|
| Faqat bitta son (hisoblagich, flag) ustida oddiy amal | sync/atomic |
Bir nechta o'zgaruvchi yoki murakkab tuzilma (struct, map) birgalikda himoyalanishi kerak | sync.Mutex |
| Bir nechta amalni "bir butun" sifatida bajarish kerak (masalan, tekshirish + o'zgartirish) | sync.Mutex |
[!WARNING]
atomicfaqat bitta amalni bo'linmas qiladi. Agar sizga "avval tekshir, keyin o'zgartir" (masalan, "agar hisoblagich 10dan kichik bo'lsa, uni oshir") kabi bir nechta amalni birgalikda atomik bajarish kerak bo'lsa,atomicning o'zi yetarli emas — bu holatdaMutexto'g'riroq tanlov, chunki u butun mantiqiy blokni (bir nechta amalni birga) himoya qila oladi.
| Vazifa | Vosita |
|---|---|
| Oddiy sonni xavfsiz oshirish/kamaytirish | atomic.Int64.Add() |
| Qiymatni xavfsiz o'qish/yozish | atomic.Int64.Load() / Store() |
| Murakkab holat yoki bir nechta amal birga | sync.Mutex |