44 / 68

sync/atomic paketi

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.

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


1. Eski (funksional) API: atomic.AddInt64()

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

kod
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).


2. Zamonaviy (turdosh) API: atomic.Int64

Go 1.19 versiyasidan boshlab, sync/atomic qulayroq, tur xavfsizligi yaxshiroq ta'minlangan API ham taqdim etadi:

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

kod
1000

[!NOTE] Yangi loyihalarda atomic.Int64, atomic.Int32, atomic.Bool, atomic.Value kabi turdosh (typed) API'ni afzal ko'ring — u eski atomic.AddInt64(&x, ...) uslubiga qaraganda xato qilish ehtimolini kamaytiradi (masalan, oddiy o'zgaruvchini tasodifan atomic funksiyasisiz o'zgartirib qo'yish kabi xatolar bu yangi API bilan deyarli imkonsiz).


3. atomic va Mutex orasida qachon qaysi birini tanlash kerak?

HolatTavsiya
Faqat bitta son (hisoblagich, flag) ustida oddiy amalsync/atomic
Bir nechta o'zgaruvchi yoki murakkab tuzilma (struct, map) birgalikda himoyalanishi keraksync.Mutex
Bir nechta amalni "bir butun" sifatida bajarish kerak (masalan, tekshirish + o'zgartirish)sync.Mutex

[!WARNING] atomic faqat 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 holatda Mutex to'g'riroq tanlov, chunki u butun mantiqiy blokni (bir nechta amalni birga) himoya qila oladi.


Xulosa: qachon qaysi vosita kerak?

VazifaVosita
Oddiy sonni xavfsiz oshirish/kamaytirishatomic.Int64.Add()
Qiymatni xavfsiz o'qish/yozishatomic.Int64.Load() / Store()
Murakkab holat yoki bir nechta amal birgasync.Mutex