التزامن وتعدد الخيوط في C#: من Thread إلى async/await
تتطلب التطبيقات الحديثة بشكل متزايد تنفيذ مهام متعددة في وقت واحد: من معالجة طلبات خادم الويب إلى الحوسبة المتوازية في البرامج المكتبية. في C# (.NET) توجد مجموعة أدوات قوية لهذا الغرض — من الخيوط التقليدية (Thread) إلى الإنشاءات عالية المستوى async/await ومكتبة Task Parallel Library (TPL). في هذه المقالة، سنشرح المفاهيم الأساسية للتزامن وتعدد الخيوط، وتطبيقاتها العملية وأفضل الممارسات.
ما هو التزامن وتعدد الخيوط؟
قبل الانتقال إلى الكود، من المهم فهم الفرق بين هذه المفاهيم:
- التزامن (Concurrency) — قدرة البرنامج على تنفيذ مهام متعددة، والتبديل بينها، مما يخلق وهم التنفيذ المتزامن. يمكن تحقيق ذلك حتى على نواة معالج واحدة.
- تعدد الخيوط (Multithreading) — شكل من أشكال التزامن، حيث يتم تنفيذ عدة خيوط (threads) فعليًا في وقت واحد على معالج متعدد النوى.
- التوازي (Parallelism) — مجموعة فرعية من تعدد الخيوط، حيث يتم تنفيذ أجزاء مختلفة من المهمة بشكل صارم في وقت واحد على نوى مختلفة.
في C#، يمكنك استخدام كل من الخيوط منخفضة المستوى والتجريدات التي تدير الخيوط تلقائيًا. يعتمد الاختيار على المهمة: لعمليات I/O-bound (العمل مع الملفات، الشبكة) يكون async/await أكثر ملاءمة، أما لـ CPU-bound (الحسابات) فاستخدم Parallel.For أو Task.
إنشاء وإدارة الخيوط: فئة Thread
فئة System.Threading.Thread هي الطريقة الأكثر انخفاضًا في المستوى للعمل مع الخيوط في C#. إنها توفر تحكمًا كاملاً، ولكنها تتطلب مزامنة دقيقة.
using System;using System.Threading;
class Program{ static void Main() { Thread thread = new Thread(DoWork); thread.Start();
// الخيط الرئيسي يواصل العمل for (int i = 0; i < 5; i++) { Console.WriteLine($"الخيط الرئيسي: {i}"); Thread.Sleep(100); }
thread.Join(); // انتظار انتهاء الخيط الفرعي Console.WriteLine("تم الانتهاء من العمل."); }
static void DoWork() { for (int i = 0; i < 5; i++) { Console.WriteLine($"الخيط الفرعي: {i}"); Thread.Sleep(150); } }}مشاكل الإدارة اليدوية للخيوط:
- تعقيد المزامنة (سباقات البيانات، deadlocks).
- تكاليف عامة عالية لإنشاء وتدمير الخيوط.
- عدم وجود دعم مدمج لإرجاع النتيجة.
لذلك، في C# الحديثة يُفضل استخدام Task Parallel Library.
Task Parallel Library (TPL) و async/await
فئة Task من مساحة الأسماء System.Threading.Tasks تمثل عملية غير متزامنة. بالاقتران مع الكلمات المفتاحية async و await، تسمح بكتابة كود غير متزامن يبدو وكأنه متزامن.
مثال أساسي مع Task
using System;using System.Threading.Tasks;
class Program{ static async Task Main() { Console.WriteLine("بداية العمل.");
// تشغيل مهمة غير متزامنة Task<int> task = CalculateAsync(10);
// بينما يتم تنفيذ المهمة، يمكننا فعل شيء آخر Console.WriteLine("نقوم بعمل آخر...");
// انتظار النتيجة int result = await task; Console.WriteLine($"النتيجة: {result}"); }
static async Task<int> CalculateAsync(int number) { await Task.Delay(1000); // محاكاة عملية طويلة return number * number; }}