Mutex qulfi ishonchli va xavfsiz, ammo u operatsion tizim darajasida goroutine'ni uyquga ketkazish va uyg'otish kabi qo'shimcha xarajatlarga ega. Agar sizga faqat oddiy hisoblagichni (counter), bayroqchani (boolean flag) yoki ko'rsatkichni xavfsiz yangilash kerak bo'lsa, Atomik amallar (Atomics) bundan ancha tezroq va samaraliroq ishlaydi!
Atomik amal (grekcha atomos — bo'linmas) — bu CPU darajasida bitta apparat ko'rsatmasi (machine instruction) bilan bajariladigan va boshqa hech bir oqim tomonidan chala holatda ko'rilishi mumkin bo'lmagan bo'linmas amaldir.
Oddiy x++ xotiradan o'qish, qo'shish va qayta yozishdan iborat 3 ta alohida amaldan tashkil topgan bo'lsa, atomik amal apparat darajasida xotira shinasini (memory bus / cache line) bloklab, bu uchtasini bitta butun sifatida bajaradi.
Go 1.19 dan boshlab atomik amallar bilan ishlash ancha qulaylashdi. Endi xom pointerlar bilan emas, balki qulay metodlarga ega tiplar bilan ishlanadi:
atomic.Int64 / atomic.Int32atomic.Uint64 / atomic.Uint32atomic.Boolatomic.Pointer[T]package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var counter atomic.Int64
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
counter.Add(1) // hech qanday Mutex shart emas, mutlaqo poygasiz!
}()
}
wg.Wait()
fmt.Println("Yakuniy natija:", counter.Load()) // Har doim 1000!
}
Asosiy metodlar:
counter.Add(delta) — qiymatni oshirish yoki kamaytirish;counter.Load() — qiymatni xavfsiz o'qish;counter.Store(val) — qiymatni xavfsiz yozish;counter.Swap(val) — yangi qiymat yozib, eski qiymatni qaytarish;counter.CompareAndSwap(old, new) — agar qiymat old ga teng bo'lsa, uni new ga almashtirish.CAS — bu barcha lock-free (qulfsiz) ma'lumotlar tuzilmalarining (lock-free queue, stack) poydevoridir:
var max atomic.Int64
func updateMax(val int64) {
for {
current := max.Load()
if val <= current {
return
}
// Agar max o'rtada boshqa goroutine tomonidan o'zgartirilmagan bo'lsa,
// uni val ga almashtiramiz:
if max.CompareAndSwap(current, val) {
return // muvaffaqiyatli yangilandi
}
// agar o'zgarib ketgan bo'lsa, sikl qaytadan aylanadi
}
}
| Xususiyat | Atomics | Mutex |
|---|---|---|
| Mos keladigan holat | Bitta o'zgaruvchi (hisoblagich, flag, pointer) | Murakkab strukturalar, bir nechta o'zaro bog'liq maydonlar |
| Tezlik | Juda yuqori (CPU apparat darajasida) | Yuqori, ammo goroutine kontekst almashtirishi mumkin |
| Deadlock xavfi | Yo'q | Ehtiyotsizlik qilinsa mavjud |
atomic.Int64 yordamida bir vaqtda 500 ta goroutine xavfsiz oshiradigan va kamaytiradigan hisoblagich tuzing.
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
var count atomic.Int64
var wg sync.WaitGroup
// TODO: 500 ta goroutine Add(1) qilsin, 200 tasi Add(-1) qilsin
// wg.Wait()
fmt.Println("Hisoblagich:", count.Load())
}
Shu bilan 02. Synchronization bo'limi to'liq yakunlandi!
Keyingi bo'lim: 03. Other topics — Concurrent Go dasturlarini professional testlash (testing) va Go runtime scheduler'ining ichki ishlash mexanizmlari (internals)!