لغة Go بالأمثلة: العدادات الذرية

الآلية الأساسية لإدارة الحالة في Go هي الاتصال عبر القنوات. رأينا ذلك مثلًا مع مجموعات العمال. لكن توجد خيارات أخرى لإدارة الحالة. سنتعرف هنا على استخدام الحزمة sync/atomic لإنشاء عدادات ذرية تصل إليها عدة روتينات Go.

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

سنستخدم نوع عدد صحيح ذري لتمثيل عدادنا ذي القيمة الموجبة دائمًا.

    var ops atomic.Uint64

ستساعدنا WaitGroup على انتظار انتهاء عمل جميع روتينات Go.

    var wg sync.WaitGroup

سنبدأ 50 روتين Go، يزيد كل منها العداد 1000 مرة بالضبط.

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

نستخدم Add لزيادة العداد ذريًا.

                ops.Add(1)
            }
        })
    }

انتظر حتى تنتهي جميع روتينات Go.

    wg.Wait()

لا تكتب أي روتينات Go في ops هنا، لكن استخدام Load يتيح قراءة القيمة ذريًا بأمان حتى أثناء تحديث روتينات Go أخرى لها ذريًا.

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

نتوقع الحصول على 50,000 عملية بالضبط. لو استخدمنا عددًا صحيحًا غير ذري وزدناه باستخدام ops++، لحصلنا غالبًا على عدد مختلف يتغير بين مرات التشغيل، لأن روتينات Go كانت ستتداخل مع بعضها. وإضافة إلى ذلك، كنا سنحصل على إخفاقات بسبب تسابق البيانات عند التشغيل باستخدام الخيار -race.

$ go run atomic-counters.go
ops: 50000

سنتعرف تاليًا على أقفال الاستبعاد المتبادل، وهي أداة أخرى لإدارة الحالة.

المثال التالي: .