الانتقال إلى المحتوى الرئيسي
الوحدة 7 · الدرس 4 من 534/50 في الدورة~14 min
دروس الوحدة (4/5)

"sync.Mutex" و"sync.WaitGroup".

القنوات هي المصطلح الأساسي لـ Go، ولكنها في بعض الأحيان أكثر تقليدية هناك حاجة إلى البدائيين. توفر الحزمة sync كائنات المزامنة، انتظر المجموعات والمرافق الأخرى لتنسيق الحالة المشتركة.

sync.Mutex: قفل حصري

Go
import "sync"

type Counter struct {
    mu    sync.Mutex
    count int
}

func (c *Counter) Inc() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.count++
}

func (c *Counter) Get() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.count
}

يدخل goroutine واحد فقط في كل مرة بين Lock() وUnlock(). النمط الإلزامي: defer mu.Unlock() مباشرة بعد Lock().

sync.RWMutex: العديد من القراء وكاتب واحد

للحالة الثقيلة للقراءة:

Go
var mu sync.RWMutex

mu.RLock()              // read lock: più goroutine simultanee OK
v := data
mu.RUnlock()

mu.Lock()               // write lock: esclusivo
data = newValue
mu.Unlock()

تكون مفيدة فقط إذا كانت القراءات تهيمن على الكتابة؛ خلاف ذلك العادية sync.Mutex أبسط وأسرع غالبًا بفضل الجزء الداخلي السفلي خلاف.

sync.WaitGroup: انتظر N goroutines

Go
var wg sync.WaitGroup

for i := 0; i < 3; i++ {
    wg.Add(1)
    go func(id int) {
        defer wg.Done()
        work(id)
    }(i)
}
wg.Wait()    // blocca finché tutti i Done() sono arrivati

سير العمل:

  1. wg.Add(N) يزيد العداد بمقدار N (قبل تشغيل goroutines).
  2. يستدعي كل goroutine wg.Done() عند انتهائه (من الأفضل عبر defer).
  3. wg.Wait() يتم حظره حتى يعود العداد إلى 0.

sync.Once: التهيئة البطيئة الآمنة لمؤشر الترابط

Go
var (
    once   sync.Once
    config *Config
)

func GetConfig() *Config {
    once.Do(func() {
        config = loadConfig()
    })
    return config
}

يتم تشغيل رد الاتصال في once.Do مرة واحدة بالضبط، حتى في حالة التزامن المكالمات. نمط التهيئة المفرد/الكسول.

Mutex مقابل القناة: متى تستخدم ماذا

الإرشادات (أيضًا من الأسئلة الشائعة الرسمية حول Go):

حالةالأداة المفضلة
تمرير ملكية البياناتقناة
توزيع العمل (طابور العمل)قناة
تنسيق goroutines المستقلةقناة
حماية حقل البنية** موتكس **
ذاكرة التخزين المؤقت / العدادMutex أو الذرية
مرجع سينجلتونsync.Once

تعد "قنوات التنسيق وكائنات المزامنة للبيانات المشتركة" قاعدة جيدة.

كاشف السباق

قم بإجراء الاختبارات باستخدام -race لاكتشاف سباقات البيانات:

Bash
go test -race ./...
go run -race main.go

يقوم كاشف السباق بقياس الثنائي ويسجل كل شيء غير متزامن الوصول إلى الذاكرة المشتركة. إنها الأداة رقم 1 للتحقق من صحة التعليمات البرمجية المتزامنة قبل الإنتاج.

جربه

تمرين#go.m7.l4.e1
المحاولات: 0جارٍ التحميل…

قم بحماية زيادة العدد باستخدام mu.Lock() + defer mu.Unlock().

جارٍ تحميل المحرر…
إظهار التلميح

تأجيل mu.Unlock() مباشرة بعد أن يضمن Lock() إطلاق سراحه حتى في حالة الذعر.

الحل متاح بعد 3 من المحاولات

تمرين#go.m7.l4.e2
المحاولات: 0جارٍ التحميل…

قم بتشغيل 3 goroutines وانتظرهم جميعًا باستخدام sync.WaitGroup (Add(3)، Done() في كل goroutine، Wait()).

جارٍ تحميل المحرر…
إظهار التلميح

أضف قبل إطلاق goroutine؛ تم ذلك داخل goroutine مع التأجيل.

الحل متاح بعد 3 من المحاولات

Quiz#go.m7.l4.e3
جاهز

ما هو النمط الموصى به لضمان تحرير كائن المزامنة (mutex)؟

Go
mu.Lock()
// ?
خيارات الإجابة

خلاصة

  • sync.Mutex يحمي الحالة المشتركة؛ دائمًا defer mu.Unlock().
  • استخدم مستقبل المؤشر لتجنب نسخ كائن المزامنة.
  • sync.RWMutex لأنماط القراء المتعددين/الكاتب الواحد (فقط إذا كان ذلك مفيدًا).
  • sync.WaitGroup: Add قبل go، Done مع defer، Wait للانتظار.
  • sync.Once: التهيئة البطيئة الآمنة لسلسلة الرسائل.
  • "قنوات التنسيق وكائنات المزامنة للبيانات المشتركة".
  • go test -race: الأداة رقم 1 لاكتشاف سباقات البيانات.