Skip to content

once

sync.Once هو أداة مزامنة في المكتبة القياسية لـ Go، يُستخدم لضمان تنفيذ دالة معينة مرة واحدة فقط في بيئة متزامنة. يُستخدم عادةً للتهيئة المؤجلة، وتهيئة الموارد العامة، وغيرها من السيناريوهات، لضمان أن عملية محددة ستُنفذ مرة واحدة فقط، حتى لو كان هناك عدة كوروتينات تنفذ بشكل متزامن.

go
package main

import (
	"fmt"
	"sync"
)

func main() {
	var wg sync.WaitGroup
	var once sync.Once
	for i := range 10 {
		wg.Add(1)
		go func() {
			defer wg.Done()
			once.Do(func() {
				fmt.Println(i)
			})
		}()
	}
	wg.Wait()
}

في الكود أعلاه، يوجد إجمالًا 10 كوروتينات، لكن في جميع الأحوال، كوروتين واحد فقط هو الذي سينفذ fmt.Println(i) داخل once.Do(). أي كوروتين تحديدًا الذي سيطبع يعتمد على أي كوروتين يصل أولًا لـ once.Do().

البنية

go
type Once struct {
    done atomic.Uint32
    m    Mutex
}

بنيته الداخلية بسيطة جدًا:

  1. done، قيمة ذرية، تُستخدم للإشارة هل تم التنفيذ من قبل
  2. m، قفل مزامنة، يُستخدم لحجب الكوروتينات الأخرى التي تريد التنفيذ

مبدأه هو القفل قبل التنفيذ، ثم تحديث done، وبعد الانتهاء فتح القفل.

Do

go
func (o *Once) Do(f func()) {
	if o.done.Load() == 0 {
		o.doSlow(f)
	}
}

func (o *Once) doSlow(f func()) {
	o.m.Lock()
	defer o.m.Unlock()
	if o.done.Load() == 0 {
		defer o.done.Store(1)
		f()
	}
}

الكود بسيط جدًا ككل، تدفقه كالتالي:

  1. تحميل القيمة الذرية مباشرة، إذا كان قد نُفذ من قبل يعود مباشرة
  2. إذا لم يُنفذ، يحاول امتلاك القفل، هنا قد يكون هناك عدة كوروتينات تتنافس على قفل المزامنة
  3. عندما ينجح الفائز في امتلاك القفل، يحتاج للحكم مرة أخرى على قيمة done، لأنه بعد امتلاكك للقفل، قد يكون غيرك قد أكمل سلسلة القفل-التنفيذ-الفتح، وأنت الآن استيقظت للتو.
  4. تنفيذ الدالة المستهدفة
  5. تحديث قيمة done
  6. تحرير القفل

ملخص

sync.Once أداة مزامنة بسيطة وفعالة جدًا، تضمن تنفيذ عملية معينة مرة واحدة فقط في بيئة متزامنة، مما يتجنب تكرار العمل أو هدر الموارد.

Golang تم تحريره بواسطة www.golangdev.cn