Menu
Coddy logo textTech
flag Ar iconالعربيةdown icon

أساسيات الـ Goroutines

جزء من قسم Object Oriented Programming في رحلة GO على Coddy — الدرس 59 من 107.

الـ goroutine هي خيط تنفيذ خفيف الوزن يتم إدارته بواسطة بيئة تشغيل Go (Go runtime). وعلى عكس الخيوط (threads) التقليدية التي تستهلك ذاكرة كبيرة، تبدأ الـ goroutines ببضعة كيلوبايتات فقط من مساحة المكدس (stack space)، مما يسمح لك بتشغيل الآلاف منها بشكل متزامن.

لبدء goroutine، ما عليك سوى وضع الكلمة المفتاحية go قبل استدعاء الدالة:

func sayHello(name string) {
    fmt.Println("Hello,", name)
}

func main() {
    go sayHello("Alice")  // يتم تشغيله بشكل متزامن
    go sayHello("Bob")    // يتم تشغيله بشكل متزامن
    
    time.Sleep(100 * time.Millisecond)
    fmt.Println("Done")
}

عندما يتم تنفيذ go sayHello("Alice")، فإنه لا ينتظر sayHello حتى ينتهي. بدلاً من ذلك، يستمر فوراً إلى السطر التالي بينما يعمل sayHello في الخلفية. هذا هو السبب في حاجتنا إلى time.Sleep—فبدونه، سيخرج main قبل اكتمال الـ goroutines.

يمكنك أيضاً تشغيل goroutines باستخدام دوال مجهولة (anonymous functions):

go func(msg string) {
    fmt.Println(msg)
}("Hello from goroutine")

تعمل الدالة main نفسها في goroutine. عندما تنتهي (returns)، يتم إنهاء جميع الـ goroutines الأخرى فوراً—ولا تتاح لها الفرصة للانتهاء. يعد استخدام time.Sleep حلاً مؤقتاً؛ في الدروس القادمة، ستتعلم تقنيات المزامنة الصحيحة باستخدام القنوات (channels) ومجموعات الانتظار (wait groups).

challenge icon

التحدي

سهل

لنقم ببناء مشغل مهام (task runner) ينفذ مهاماً متعددة بشكل متزامن باستخدام الـ goroutines. ستقوم بإنشاء نظام بسيط حيث تعمل المهام في الخلفية بينما يستمر البرنامج الرئيسي، مما يوضح كيف تمكن الـ goroutines من التنفيذ المتزامن.

ستقوم بتنظيم الكود الخاص بك عبر ملفين:

  • tasks.go: حدد منطق تنفيذ المهام الخاص بك.

    أنشئ هيكلاً Task يحتوي على حقول Name (string) و Duration (int، يمثل المللي ثانية).

    قم بتنفيذ تابع Run() على Task يحاكي العمل عن طريق النوم (sleeping) لمدة المهمة، ثم يطبع: Task [Name] completed

    أنشئ دالة RunAllTasks(tasks []Task) تقوم بتشغيل تابع Run() لكل مهمة كـ goroutine. بعد تشغيل جميع الـ goroutines، يجب أن تنام الدالة لمدة 500 مللي ثانية للسماح لها بالاكتمال، ثم تطبع: All tasks launched

  • main.go: قراءة معلومات المهام وتنسيق التنفيذ المتزامن.

    اقرأ عدد المهام، ثم اقرأ اسم كل مهمة ومدتها. أنشئ المهام ومررها إلى RunAllTasks. بعد عودتها، اطبع: Main function done

سيتم توفير المدخلات التالية:

  • السطر 1: عدد المهام (عدد صحيح)
  • الأسطر التالية: لكل مهمة، سطران - اسم المهمة، ثم مدتها بالمللي ثانية

على سبيل المثال، إذا كان المعطى:

3
Download
100
Process
50
Upload
75

يجب أن يظهر مخرجك اكتمال المهام (قد يختلف الترتيب بسبب التزامن)، متبوعاً برسائل الاكتمال:

Task Process completed
Task Upload completed
Task Download completed
All tasks launched
Main function done

ملاحظة: نظراً لأن الـ goroutines تعمل بشكل متزامن، فإن المهام ذات المدد الأقصر تنتهي عادةً أولاً. يعتمد ترتيب اكتمال المهام على مددها - المهام الأقصر تكتمل قبل المهام الأطول.

استخدم time.Sleep مع time.Duration و time.Millisecond للتأخيرات. تذكر أنه بدون النوم في RunAllTasks، ستعود الدالة قبل أن تنهي الـ goroutines عملها.

ورقة مرجعية

الـ goroutine هي خيط تنفيذ (thread) خفيف الوزن تتم إدارته بواسطة بيئة تشغيل Go (Go runtime). تبدأ الـ goroutines بمساحة مكدس (stack space) تبلغ بضعة كيلوبايتات فقط، مما يسمح لك بتشغيل الآلاف منها بشكل متزامن.

لبدء goroutine، ضع الكلمة المفتاحية go قبل استدعاء الدالة:

func sayHello(name string) {
    fmt.Println("Hello,", name)
}

func main() {
    go sayHello("Alice")  // يتم تشغيله بشكل متزامن
    go sayHello("Bob")    // يتم تشغيله بشكل متزامن
    
    time.Sleep(100 * time.Millisecond)
    fmt.Println("Done")
}

عندما يتم تنفيذ go sayHello("Alice")، فإنه لا ينتظر انتهاء sayHello. بدلاً من ذلك، ينتقل فوراً إلى السطر التالي بينما يتم تشغيل sayHello في الخلفية.

يمكنك إطلاق goroutines باستخدام دوال مجهولة (anonymous functions):

go func(msg string) {
    fmt.Println(msg)
}("Hello from goroutine")

الدالة main نفسها تعمل في goroutine. عندما تنتهي، يتم إنهاء جميع الـ goroutines الأخرى فوراً. استخدم time.Sleep للسماح للـ goroutines بالاكتمال قبل خروج الدالة main.

جرّب بنفسك

package main

import (
	"fmt"
)

func main() {
	// قراءة عدد المهام
	var numTasks int
	fmt.Scanln(&numTasks)

	// TODO: إنشاء شريحة (slice) للاحتفاظ بالمهام
	
	// TODO: قراءة اسم كل مهمة ومدتها
	// لكل مهمة:
	//   - قراءة اسم المهمة
	//   - قراءة المدة (كعدد صحيح)
	//   - إنشاء مهمة (Task) وإضافتها إلى الشريحة الخاصة بك

	// TODO: استدعاء RunAllTasks مع شريحة المهام الخاصة بك

	// TODO: طباعة "Main function done"
}
quiz iconاختبر نفسك

يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.

جميع دروس Object Oriented Programming