دروس الوحدة (1/5)
Goroutines: التوازي خفيف الوزن
goroutine هي وحدة تنفيذ متزامنة يديرها 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).
الرئيسي لا ينتظر
func main() {
go func() { fmt.Println("ciao dalla goroutine") }()
// main esce subito: niente garanzia che la goroutine completi
}عندما يعود main، تنتهي العملية: لا يتم الحصول على goroutines
"لإنهاء عملهم"، يتم قتلهم ببساطة مع
عملية. مطلوب مزامنة صريحة.
التزامن: معاينة
الأدوات الثلاث التي ستراها في الدروس القادمة:
// 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، كانت هذه مشكلة متكررة:
for i := 0; i < 3; i++ {
go func() { fmt.Println(i) }() // stampa "3 3 3" su versioni vecchie
}من Go 1.22 تم إنشاء المتغير for حديثًا في كل منها
التكرار، لذلك يتم إصلاح الخلل افتراضيًا. في قواعد التعليمات البرمجية القديمة سوف تفعل ذلك
لا تزال ترى نمط الإصلاح:
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 عبر المواضيع عند الحاجة.
جربه
قم بتشغيل وظيفة الترحيب كإجراء روتيني وانتظر 100 مللي ثانية مع مرور الوقت. قم بالنوم قبل الخروج من الوضع الرئيسي.
إظهار التلميح
`اذهب` أمام استدعاء دالة يؤدي إلى تشغيل goroutine.
الحل متاح بعد 3 من المحاولات
قم بتشغيل وظيفة مجهولة باعتبارها goroutine الذي يطبع "hi".
إظهار التلميح
يتم استدعاء دالة مجهولة مع لاحقة `()`: `go func(){...}()`.
الحل متاح بعد 3 من المحاولات
ماذا يحدث إذا تم الخروج "الرئيسي" قبل اكتمال goroutine؟
func main() {
go work()
// main ritorna immediatamente
}خلاصة
go f()يُطلق f() بشكل متزامن؛ يعود على الفور.- Goroutine = ~2 كيلو بايت أولي، مُضاعف على عدد قليل من سلاسل عمليات نظام التشغيل.
mainلا ينتظر: أنت بحاجة إلى المزامنة (القناة/مجموعة الانتظار/السياق).time.Sleepتعليمي فقط، ولا يُستخدم مطلقًا للمزامنة في الإنتاج.- من Go 1.22، يكون المتغير
for"جديدًا" في كل تكرار (لم يعد هناك خطأ "3 3 3").