قوائم المهام في Go: دليل شامل للمبتدئين

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة في قوائم المهام بلغة Go

\n

قوائم المهام (task queues) هي أحد الأنماط الأساسية في تطوير التطبيقات عالية التحميل. فهي تسمح بتنظيم المعالجة غير المتزامنة للبيانات، وتوزيع الحمل، وزيادة تحمل الأخطاء في النظام. في لغة Go، بفضل الآليات المدمجة للتوازي - goroutines والقنوات - يصبح تنفيذ قوائم المهام أنيقًا وفعالًا بشكل لا يصدق.

\n

في هذه المقالة، سنشرح كيفية إنشاء قوائم المهام في Go من الصفر. ستتعرف على ما هي goroutines والقنوات، وكيفية تنظيم مجموعة من العمال (worker pool) ومعالجة المهام بالتوازي. المادة موجهة للمطورين المبتدئين الذين لديهم معرفة بأساسيات بناء جملة Go.

\n\n

الأساسيات: goroutines والقنوات

\n

قبل الانتقال إلى قوائم المهام، من المهم فهم مفهومين أساسيين في Go:

\n
    \n
  • Goroutine — خيط تنفيذ خفيف الوزن. يتم تشغيله باستخدام الكلمة المفتاحية go قبل استدعاء الدالة.
  • \n
  • قناة (channel) — "قناة" محددة النوع لنقل البيانات بين goroutines. تسمح بمزامنة عملها.
  • \n
\n

مثال على أبسط goroutine وقناة:

\n
package main\n\nimport "fmt"\n\nfunc main() {\n    ch := make(chan string)\n\n    go func() {\n        ch <- "مرحبًا من goroutine!"\n    }()\n\n    msg := <-ch\n    fmt.Println(msg)\n}\n
\n

في هذا المثال، أنشأنا قناة ch، وقمنا بتشغيل goroutine مجهولة ترسل رسالة إليها، ثم استقبلت goroutine الرئيسية هذه الرسالة. على هذه الآلية تُبنى قوائم المهام.

\n\n

تنفيذ قائمة مهام بسيطة

\n

أبسط قائمة مهام هي قناة نضع فيها المهام، وواحدة أو أكثر من goroutines تقوم بمعالجتها. لنلقِ نظرة على مثال أساسي:

\n
package main\n\nimport (\n    "fmt"\n    "time"\n)\n\nfunc worker(id int, tasks <-chan int) {\n    for task := range tasks {\n        fmt.Printf("العامل %d بدأ المهمة %d\\n", id, task)\n        time.Sleep(time.Second) // محاكاة العمل\n        fmt.Printf("العامل %d أكمل المهمة %d\\n", id, task)\n    }\n}\n\nfunc main() {\n    tasks := make(chan int, 10) // قناة مخزنة مؤقتًا\n\n    // تشغيل عامل واحد\n    go worker(1, tasks)\n\n    // إرسال 5 مهام\n    for i := 1; i <= 5; i++ {\n        tasks <- i\n    }\n\n    close(tasks) // إغلاق القناة\n    time.Sleep(2 * time.Second) // إعطاء وقت للمعالجة\n}\n
\n

هنا worker هي دالة تقرأ من القناة tasks. القناة مخزنة مؤقتًا (سعة 10)، لذا يمكننا إرسال عدة مهام دون حظر. بعد إغلاق القناة، ستنتهي حلقة for range تلقائيًا.

\n

لاحظ: استخدمنا <-chan int - وهي قناة للقراءة فقط. هذه ممارسة جيدة توضح بوضوح أن العامل لا يجب أن يرسل بيانات إلى هذه القناة.

\n\n

القنوات المخزنة مؤقتًا وغير المخزنة

\n

في المثال أعلاه، استخدمنا قناة مخزنة مؤقتًا (make(chan int, 10)). الفرق بين أنواع القنوات:

\n
    \n
  • قناة غير مخزنة — يتم حظر الإرسال حتى يقرأ المستقبل البيانات. مثالية للمزامنة.
  • \n
  • قناة مخزنة مؤقتًا — يتم حظر الإرسال فقط عندما يكون المخزن ممتلئًا. تسمح بتجميع المهام وتقليل التأخير.
  • \n
\n

لقوائم المهام، عادةً ما تُستخدم القنوات المخزنة مؤقتًا حتى يتمكن المنتج من إرسال المهمة بسرعة ومواصلة العمل دون انتظار تنفيذها.

\n\n

مجموعة العمال (Worker Pool) في Go

\n

عامل واحد جيد، لكن عدة عمال أفضل. مجموعة العمال تسمح بمعالجة المهام بالتوازي باستخدام جميع نوى المعالج. إليك كيفية تنفيذ ذلك:

\n
package main\n\nimport (\n    "fmt"\n    "sync"\n    "time"\n)\n\nfunc worker(id int, tasks <-chan int, wg *sync.WaitGroup) {\n    defer wg.Done()\n    fo                

المدونات

توصيات الكتب