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

Goroutines: التوازي خفيف الوزن

goroutine هي وحدة تنفيذ متزامنة يديرها Go وقت التشغيل. يمكنك تشغيل واحدة باستخدام الكلمة الأساسية go أمام إحدى الوظائف اتصل:

Go
go greet("Anna")         // chiamata a funzione esistente
go func() {              // funzione anonima
    fmt.Println("hi")
}()

تُرجع عبارة go ** على الفور **: يبدأ goroutine في بالتوازي والمتصل يستمر.

لماذا هم "خفيفة الوزن"

  • المكدس الأولي ~2 كيلو بايت (ينمو ديناميكيًا، حتى مئات ميغابايت إذا لزم الأمر).
  • الإرسال المتعدد إلى عدد صغير من سلاسل عمليات نظام التشغيل خلال وقت التشغيل (GOMAXPROCS).
  • الآلاف/الملايين من goroutines روتينية؛ مع مؤشرات ترابط نظام التشغيل سيكون من المستحيل.

من الناحية النظرية هو "أطلق وانسى بشكل متزامن"، ولكن مع كل الأدوات الاصطلاحية للتنسيق (القنوات، sync.WaitGroup، محمي_INLINE_5).

الرئيسي لا ينتظر

Go
func main() {
    go func() { fmt.Println("ciao dalla goroutine") }()
    // main esce subito: niente garanzia che la goroutine completi
}

عندما يعود main، تنتهي العملية: لا يتم الحصول على goroutines "لإنهاء عملهم"، يتم قتلهم ببساطة مع عملية. مطلوب مزامنة صريحة.

التزامن: معاينة

الأدوات الثلاث التي ستراها في الدروس القادمة:

Go
// 1) Channel: la goroutine "segnala" via canale
done := make(chan struct{})
go func() {
    work()
    done <- struct{}{}
}()
<-done   // attendi

// 2) sync.WaitGroup: N goroutine
var wg sync.WaitGroup
wg.Add(1)
go func() { defer wg.Done(); work() }()
wg.Wait()

// 3) context: cancellazione/timeout propagati

خطأ الإغلاق "الكلاسيكي" في الحلقة

حتى Go 1.21، كانت هذه مشكلة متكررة:

Go
for i := 0; i < 3; i++ {
    go func() { fmt.Println(i) }()  // stampa "3 3 3" su versioni vecchie
}

من Go 1.22 تم إنشاء المتغير for حديثًا في كل منها التكرار، لذلك يتم إصلاح الخلل افتراضيًا. في قواعد التعليمات البرمجية القديمة سوف تفعل ذلك لا تزال ترى نمط الإصلاح:

Go
for i := 0; i < 3; i++ {
    i := i           // shadow esplicito
    go func() { fmt.Println(i) }()
}
// oppure passa come argomento:
for i := 0; i < 3; i++ {
    go func(n int) { fmt.Println(n) }(i)
}

Goroutine!= مؤشر ترابط نظام التشغيل

الجانبغوروتينموضوع نظام التشغيل
المكدس الأولي~2 كيلو بايت1–8 ميجابايت (مخصصة في البداية)
الخلقنانو ثانيةميكروثانية
جدولةاذهب وقت التشغيل (التعاونية)نواة (استباقية)
العد العملي100 ألف+بضعة آلاف

يمكن أن يستضيف مؤشر ترابط نظام تشغيل واحد العديد من goroutines. يتحرك وقت التشغيل goroutines عبر المواضيع عند الحاجة.

جربه

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

قم بتشغيل وظيفة الترحيب كإجراء روتيني وانتظر 100 مللي ثانية مع مرور الوقت. قم بالنوم قبل الخروج من الوضع الرئيسي.

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

`اذهب` أمام استدعاء دالة يؤدي إلى تشغيل goroutine.

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

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

قم بتشغيل وظيفة مجهولة باعتبارها goroutine الذي يطبع "hi".

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

يتم استدعاء دالة مجهولة مع لاحقة `()`: `go func(){...}()`.

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

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

ماذا يحدث إذا تم الخروج "الرئيسي" قبل اكتمال goroutine؟

Go
func main() {
  go work()
  // main ritorna immediatamente
}
خيارات الإجابة

خلاصة

  • go f() يُطلق f() بشكل متزامن؛ يعود على الفور.
  • Goroutine = ~2 كيلو بايت أولي، مُضاعف على عدد قليل من سلاسل عمليات نظام التشغيل.
  • main لا ينتظر: أنت بحاجة إلى المزامنة (القناة/مجموعة الانتظار/السياق).
  • time.Sleep تعليمي فقط، ولا يُستخدم مطلقًا للمزامنة في الإنتاج.
  • من Go 1.22، يكون المتغير for "جديدًا" في كل تكرار (لم يعد هناك خطأ "3 3 3").