دروس الوحدة (4/5)
"sync.Mutex" و"sync.WaitGroup".
القنوات هي المصطلح الأساسي لـ Go، ولكنها في بعض الأحيان أكثر تقليدية
هناك حاجة إلى البدائيين. توفر الحزمة sync كائنات المزامنة، انتظر
المجموعات والمرافق الأخرى لتنسيق الحالة المشتركة.
sync.Mutex: قفل حصري
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: العديد من القراء وكاتب واحد
للحالة الثقيلة للقراءة:
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
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سير العمل:
wg.Add(N)يزيد العداد بمقدار N (قبل تشغيل goroutines).- يستدعي كل goroutine
wg.Done()عند انتهائه (من الأفضل عبرdefer). wg.Wait()يتم حظره حتى يعود العداد إلى 0.
sync.Once: التهيئة البطيئة الآمنة لمؤشر الترابط
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 لاكتشاف سباقات البيانات:
go test -race ./...
go run -race main.goيقوم كاشف السباق بقياس الثنائي ويسجل كل شيء غير متزامن الوصول إلى الذاكرة المشتركة. إنها الأداة رقم 1 للتحقق من صحة التعليمات البرمجية المتزامنة قبل الإنتاج.
جربه
قم بحماية زيادة العدد باستخدام mu.Lock() + defer mu.Unlock().
إظهار التلميح
تأجيل mu.Unlock() مباشرة بعد أن يضمن Lock() إطلاق سراحه حتى في حالة الذعر.
الحل متاح بعد 3 من المحاولات
قم بتشغيل 3 goroutines وانتظرهم جميعًا باستخدام sync.WaitGroup (Add(3)، Done() في كل goroutine، Wait()).
إظهار التلميح
أضف قبل إطلاق goroutine؛ تم ذلك داخل goroutine مع التأجيل.
الحل متاح بعد 3 من المحاولات
ما هو النمط الموصى به لضمان تحرير كائن المزامنة (mutex)؟
mu.Lock()
// ?خلاصة
sync.Mutexيحمي الحالة المشتركة؛ دائمًاdefer mu.Unlock().- استخدم مستقبل المؤشر لتجنب نسخ كائن المزامنة.
sync.RWMutexلأنماط القراء المتعددين/الكاتب الواحد (فقط إذا كان ذلك مفيدًا).sync.WaitGroup:Addقبلgo،Doneمعdefer،Waitللانتظار.sync.Once: التهيئة البطيئة الآمنة لسلسلة الرسائل.- "قنوات التنسيق وكائنات المزامنة للبيانات المشتركة".
go test -race: الأداة رقم 1 لاكتشاف سباقات البيانات.