التزامن وتعدد الخيوط في C#: دليل شامل

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

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

ابدأ الدورة
```html

التزامن وتعدد الخيوط في C#: دليل شامل

تتطلب التطبيقات الحديثة أداءً عاليًا واستجابة سريعة. التزامن (concurrency) وتعدد الخيوط (multithreading) في C# هما أدوات أساسية تسمح بتنفيذ مهام متعددة في وقت واحد، والاستفادة بكفاءة من المعالجات متعددة النوى، وتحسين تجربة المستخدم. في هذه المقالة، سنشرح المفاهيم الأساسية، بدءًا من الخيوط التقليدية وصولاً إلى async/await الحديث وParallel LINQ.

أساسيات تعدد الخيوط: كلاس Thread ومجموعة الخيوط

يبدأ تعدد الخيوط في C# بالكلاس System.Threading.Thread. يمثل كل خيط مسار تنفيذ منفصل داخل العملية. ومع ذلك، فإن إنشاء الخيوط يدويًا هو عملية مكلفة. للتحسين، يتم استخدام مجموعة الخيوط (ThreadPool) التي تدير إعادة استخدام الخيوط.

مثال على إنشاء خيط يدويًا:

using System;
using System.Threading;

class Program
{
    static void Main()
    {
        Thread thread = new Thread(DoWork);
        thread.Start();
        Console.WriteLine("الخيط الرئيسي يعمل...");
        thread.Join(); // انتظار الانتهاء
    }

    static void DoWork()
    {
        Console.WriteLine("الخيط يقوم بالعمل.");
    }
}

للاستخدام الأسهل، يُوصى باستخدام ThreadPool:

ThreadPool.QueueUserWorkItem(state => {
    Console.WriteLine("مهمة في مجموعة الخيوط.");
});

هام: كلاس Thread لديه واجهة برمجة تطبيقات منخفضة المستوى ولا يدعم إرجاع النتيجة. للتطبيقات الحديثة، استخدم Task.

Task وTPL (مكتبة المهام المتوازية)

توفر مكتبة TPL (System.Threading.Tasks) تجريدًا عالي المستوى — Task. يمثل Task عملية غير متزامنة يمكنها إرجاع قيمة. هذا هو الأساس للتزامن الحديث في C#.

مثال على Task بدون إرجاع قيمة:

using System;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        Task task = Task.Run(() => DoWork());
        await task;
        Console.WriteLine("تم الانتهاء.");
    }

    static void DoWork()
    {
        Console.WriteLine("عمل في Task.");
    }
}

Task مع إرجاع قيمة:

Task<int> task = Task.Run(() => {
    int result = 0;
    for (int i = 0; i < 1000000; i++) result += i;
    return result;
});
int sum = await task;
Console.WriteLine($"المجموع: {sum}");

تتضمن TPL أيضًا Parallel.For و Parallel.ForEach للمعالجة المتوازية للمجموعات:

Parallel.For(0, 10, i => {
    Console.WriteLine($"التكرار {i} على الخيط {Thread.CurrentThread.ManagedThreadId}");
});

Async/await: البرمجة غير المتزامنة

تعمل الكلمات المفتاحية async و await على تبسيط كتابة الكود غير المتزامن، مما يجعله مشابهًا للكود المتزامن. لا تقوم بإنشاء خيوط جديدة، بل تقوم بتبديل سياق التنفيذ بكفاءة، مما يحرر الخيط لمهام أخرى.

مثال على دالة غير متزامنة:

using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        string data = await FetchDataAsync("https://example.com");
        Console.WriteLine(data);
    }

    static async Task<string> FetchDataAsync(string url)
    {
        using HttpClient client = new HttpClient();
        string result = await client.GetStringAsync(url);
        return result;
    }
}

قواعد الاستخدام:

  • يجب أن تعيد الدالة التي تحتوي على async إما Task أو Task<T> أو void (فقط لمعالجات الأحداث).
  • يجب استخدام await فقط داخل دالة معرفة بـ async.
  • تجنب استخدام .Result أو .Wait() على Task لأنها قد تسبب deadlock.
```

المدونات

توصيات الكتب