Go by Example: Atomic Counters

Go’da holatni boshqarishning asosiy mexanizmi kanallar orqali aloqa qilishdir. Buni biz, masalan, worker pools misolida ko’rgan edik. Ammo holatni boshqarishning yana bir nechta usullari mavjud. Bu yerda biz bir nechta goroutinalar tomonidan foydalaniladigan atomik hisoblagichlar uchun sync/atomic paketidan foydalanishni ko’rib chiqamiz.

package main
import (
    "fmt"
    "sync"
    "sync/atomic"
)
func main() {

(Har doim musbat bo’lgan) hisoblagichimizni ifodalash uchun atomik butun son tipidan foydalanamiz.

    var ops atomic.Uint64

WaitGroup barcha goroutinalar o’z ishlarini tugatishini kutishimizga yordam beradi.

    var wg sync.WaitGroup

Biz 50 ta goroutina ishga tushiramiz, ularning har biri hisoblagichni aniq 1000 marta oshiradi.

    for range 50 {
        wg.Go(func() {
            for range 1000 {

Hisoblagichni atomik tarzda oshirish uchun Adddan foydalanamiz.

                ops.Add(1)
            }
        })
    }

Barcha goroutinalar tugaguncha kutamiz.

    wg.Wait()

Bu yerda hech qaysi goroutina ‘ops’ga yozmayapti, lekin Load yordamida boshqa goroutinalar uni (atomik tarzda) yangilab turgan paytda ham qiymatni atomik tarzda xavfsiz o’qish mumkin.

    fmt.Println("ops:", ops.Load())
}

Biz aniq 50,000 ta amal bo’lishini kutamiz. Agar atomik bo’lmagan butun sondan foydalanib, uni ops++ bilan oshirganimizda edi, ehtimol har gal ishga tushirishda o’zgarib turadigan boshqacha son olardik, chunki goroutinalar bir-biriga xalal berardi. Bundan tashqari, -race bayrog’i bilan ishga tushirganimizda data race xatoliklariga duch kelardik.

$ go run atomic-counters.go
ops: 50000

Keyingi misolda holatni boshqarishning yana bir vositasi bo’lgan mutexlarni ko’rib chiqamiz.

Keyingi misol: .