Logo

معمل نظام التشغيل - الأسبوع 7

12 دقيقة قراءة
شرائح الدرس
1 / 15

معمل نظم التشغيل - الأسبوع السابع

الخيوط والتزامن في C#: إنشاء الخيوط، وتجنب حالات التسابق، واستخدام ThreadPool

الـ Threading والتزامن (Concurrency) في C#


جدول المحتويات

  1. مقدمة
  2. البرنامج والـ Process والـ Thread
  3. أساسيات الـ Threading
  4. التنفيذ المتزامن (Synchronous) مقابل غير المتزامن (Asynchronous)
  5. دورة حياة الـ Thread وإدارتها
  6. حالات التسابق والمزامنة
  7. الـ Thread Pool
  8. تحليل الأداء: أحادي مقابل متعدد الـ Threads
  9. أمثلة تطبيقية

مقدمة

يستكشف هذا المعمل مفاهيم الـ threading في C#، ويوضح كيفية إنشاء الـ threads وإدارتها ومزامنتها في تطبيقات .NET. يتيح الـ threading التنفيذ المتزامن لعدة عمليات في آن واحد، مما يُحسِّن أداء التطبيق واستجابته.

أهداف التعلم:

  • فهم الفرق بين البرنامج والـ process والـ thread
  • إنشاء الـ threads وإدارتها في C#
  • تنفيذ آليات مزامنة الـ threads
  • التعرف على حالات التسابق (Race Conditions) ومنعها
  • مقارنة الأداء بين التنفيذ الأحادي والمتعدد الـ threads
  • استخدام الـ ThreadPool لإدارة المهام بكفاءة

البرنامج والـ Process والـ Thread

التعريفات

graph TB
    A[Program] -->|Loaded into memory| B[Process]
    B -->|Contains one or more| C[Thread]
    
    style A fill:#e1f5ff
    style B fill:#fff4e1
    style C fill:#e8f5e9

البرنامج (Program): مجموعة ثابتة من التعليمات مُخزَّنة على القرص (ملف تنفيذي). يحتوي على الكود والبيانات لكنه لا يُنفَّذ من تلقاء نفسه.

الـ Process: نسخة قيد التنفيذ من برنامج ما. عند تشغيل برنامج، ينشئ نظام التشغيل process تتضمن:

  • مساحة الذاكرة (الكود، البيانات، الـ heap، الـ stack)
  • موارد النظام (مقابض الملفات، اتصالات الشبكة)
  • thread واحدة على الأقل من التنفيذ

الـ Thread: أصغر وحدة تنفيذ داخل الـ process. تشترك عدة threads داخل نفس الـ process في:

  • مساحة الذاكرة
  • الموارد
  • واصفات الملفات (File Descriptors)

بينما تمتلك كل thread ما يخصها من:

  • عدَّاد البرنامج (Program Counter)
  • الـ Stack
  • مجموعة السجلات (Register Set)

مخطط العلاقة

graph LR
    A[Operating System] --> B[Process 1]
    A --> C[Process 2]
    B --> D[Thread 1.1]
    B --> E[Thread 1.2]
    B --> F[Thread 1.3]
    C --> G[Thread 2.1]
    C --> H[Thread 2.2]
    
    style A fill:#f9f9f9
    style B fill:#e3f2fd
    style C fill:#e3f2fd
    style D fill:#c8e6c9
    style E fill:#c8e6c9
    style F fill:#c8e6c9
    style G fill:#c8e6c9
    style H fill:#c8e6c9

أساسيات الـ Threading

إنشاء thread وتشغيلها

الخطوات الأساسية للتعامل مع الـ threads في C#:

flowchart TD
    A[Create Thread Object] --> B[Assign Method to Execute]
    B --> C[Start Thread]
    C --> D{Need to Wait?}
    D -->|Yes| E[Call Join]
    D -->|No| F[Continue Main Thread]
    E --> G[Thread Completes]
    F --> G
    
    style A fill:#e1f5ff
    style B fill:#e1f5ff
    style C fill:#fff4e1
    style E fill:#ffe1e1
    style G fill:#e8f5e9

المثال 1: إنشاء thread أساسية

using System;
using System.Threading;
 
class Program
{
    static void Main()
    {
        Console.WriteLine("Main thread started.");
 
        // Create a new thread, passing in the method it should execute
        Thread myThread = new Thread(PrintNumbers);
 
        // Start the thread
        myThread.Start();
        
        // Using Fork-Join style with threads
        // Wait for the new thread to complete
        myThread.Join();
 
        Console.WriteLine("Main thread completed.");
    }
 
    // Method to be executed in a separate thread
    static void PrintNumbers()
    {
        Console.WriteLine("Secondary thread started.");
 
        // Loop to print numbers from 1 to 5
        for (int i = 1; i <= 5; i++)
        {
            Console.WriteLine($"Number: {i}");
            Thread.Sleep(500); // Pause for 500ms to simulate work
        }
 
        Console.WriteLine("Secondary thread completed.");
    }
}

نقاط أساسية:

  • يأخذ بانِي (Constructor) الصنف Thread مفوِّض تابع (ThreadStart)
  • Start() يبدأ تنفيذ الـ thread
  • Join() يوقف الـ thread المُستدعية حتى تنتهي الـ thread المستهدفة (نمط Fork-Join)
  • Thread.Sleep() يُوقِف التنفيذ مؤقتًا لمدة زمنية محددة

الناتج المتوقع:

Main thread started.
Secondary thread started.
Number: 1
Number: 2
Number: 3
Number: 4
Number: 5
Secondary thread completed.
Main thread completed.

التنفيذ المتزامن (Synchronous) مقابل غير المتزامن (Asynchronous)

التنفيذ المتزامن (Synchronous)

تُنفَّذ العمليات بالتتابع، واحدة تلو الأخرى. يجب أن تكتمل كل عملية قبل بدء التالية.

sequenceDiagram
    participant Main
    participant Task1
    participant Task2
    
    Main->>Task1: Execute
    Task1-->>Main: Complete
    Main->>Task2: Execute
    Task2-->>Main: Complete
    Main->>Main: Continue

التنفيذ غير المتزامن (Asynchronous)

يمكن تنفيذ عدة عمليات في آن واحد. يمكن أن تبدأ عملية قبل اكتمال العمليات السابقة.

sequenceDiagram
    participant Main
    participant Thread1
    participant Thread2
    
    Main->>Thread1: Start
    Main->>Thread2: Start
    Thread1-->>Main: Working...
    Thread2-->>Main: Working...
    Thread1-->>Main: Complete
    Thread2-->>Main: Complete

مثال: تنفيذ متتابع مقابل تنفيذ متوازٍ

using System.Diagnostics;
 
namespace Threading;
 
public class Program
{
    public static void Main()
    {
        // Measure execution time
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();
        
        PingPong();
        
        stopwatch.Stop();
        Console.WriteLine($"Time elapsed: {stopwatch.ElapsedMilliseconds} ms");
    }
 
    private static void PingPong()
    {
         // Sequential execution (Synchronous)
         // Ping();
         // Pong();
        
         Console.WriteLine("--------------------");
        
         // Parallel execution (Asynchronous)
         Thread pingThread = new Thread(Ping);
         Thread pongThread = new Thread(Pong);
        
         // Background vs Foreground threads
         pingThread.IsBackground = true;
         pongThread.IsBackground = true;
        
         // Start the threads
         pingThread.Start();
         pongThread.Start();
        
         Console.WriteLine("--------------------");
        
         // Wait for both threads to complete
         pingThread.Join();
         pongThread.Join();
        
         Console.WriteLine("Join finished");
    }
    
    // Loop to be executed by the thread
    private static void Ping()
    {
        for (int i = 0; i < 100; i++)
        {
            Console.WriteLine($"Ping: {i}");
        }
    }
    
    private static void Pong()
    {
        for (int i = 0; i < 100; i++)
        {
            Console.WriteLine($"Pong: {i}");
        }
    }
}

ملاحظات:

  • التنفيذ المتتابع: تكتمل Ping بالكامل قبل أن تبدأ Pong
  • التنفيذ المتوازي: تُنفَّذ Ping وPong في آن واحد، ويتداخل الناتج
  • عادةً ما يكون التنفيذ المتوازي أسرع في المهام المرتبطة بعمليات الإدخال/الإخراج (I/O-bound) أو المهام المستقلة

دورة حياة الـ Thread وإدارتها

حالات الـ Thread

stateDiagram-v2
    [*] --> Unstarted: Create Thread
    Unstarted --> Running: Start()
    Running --> WaitSleepJoin: Sleep/Join/Wait
    WaitSleepJoin --> Running: Wake up
    Running --> Stopped: Complete
    Stopped --> [*]
    
    note right of Running
        Thread is executing
    end note
    
    note right of WaitSleepJoin
        Thread is blocked
    end note

الـ Threads في الخلفية (Background) مقابل الـ Threads في الواجهة (Foreground)

الـ Threads في الواجهة (Foreground) (الافتراضية):

  • تُبقي التطبيق قيد التشغيل
  • لا يُغلَق التطبيق حتى تنتهي جميع threads الواجهة

الـ Threads في الخلفية (Background):

  • لا تمنع التطبيق من الإغلاق
  • تُنهى تلقائيًا عند انتهاء جميع threads الواجهة
// Setting thread as background
pingThread.IsBackground = true;
pongThread.IsBackground = true;

توابع الـ Thread

التوابع الشائعة:

التابعالوصف
Start()يبدأ تنفيذ الـ thread
Join()يوقف الـ thread المستدعية حتى تنتهي الـ thread المستهدفة
Sleep(ms)يُوقِف الـ thread مؤقتًا لعدد الميلي ثانية المحدد
Abort()❌ مُهمَل (Deprecated) - ينهي الـ thread قسرًا (غير مُوصى به)

ملاحظات مهمة:

  • لم يعد Abort() مدعومًا في إصدارات .NET الحديثة (خطير وغير متوقع النتائج)
  • لا يمكن استدعاء Start() على نفس الـ thread أكثر من مرة (يُطلِق استثناءً)
  • استخدم Join() لتنفيذ نقاط المزامنة

حالات التسابق والمزامنة

ما هي حالة التسابق (Race Condition)؟

تحدث حالة التسابق عندما تصل عدة threads إلى بيانات مشتركة في نفس الوقت، وتعتمد النتيجة على التوقيت غير المتوقع لتنفيذ الـ threads.

flowchart LR
    A[Thread 1] --> C[Shared Variable: counter = 0]
    B[Thread 2] --> C
    C --> D[Thread 1 reads: 0]
    C --> E[Thread 2 reads: 0]
    D --> F[Thread 1 increments: 1]
    E --> G[Thread 2 increments: 1]
    F --> H[Thread 1 writes: 1]
    G --> I[Thread 2 writes: 1]
    H --> J[Final value: 1]
    I --> J
    
    style J fill:#ffcccc

القيمة المتوقعة: counter = 2
القيمة الفعلية: counter = 1 (حالة تسابق!)

مثال: حالة تسابق مع ThreadPool

using System;
using System.Threading;
 
class Program
{
    // Shared variable to demonstrate race condition
    static int counter = 0;
    static object lockObject = new object(); // Lock object to prevent race conditions
 
    static void Main()
    {
        Console.WriteLine("Starting Thread Pool Example with Increment and Decrement Tasks");
 
        // ThreadPool example - queue multiple tasks to increment and decrement counter
        ThreadPool.SetMaxThreads(50, 50);
        ThreadPool.QueueUserWorkItem(IncrementCounter);
        ThreadPool.QueueUserWorkItem(DecrementCounter);
        ThreadPool.QueueUserWorkItem(IncrementCounter);
        ThreadPool.QueueUserWorkItem(DecrementCounter);
 
        // Wait for a moment to see race condition effect
        Thread.Sleep(500); // Note: this is for demo purposes, not ideal in real applications
 
        Console.WriteLine($"Final counter value after Thread Pool (may vary due to race condition): {counter}");
    }
 
    // Method to increment the counter in ThreadPool
    static void IncrementCounter(object state)
    {
        // WITHOUT LOCK (Race condition can occur)
        for (int i = 0; i < 100; i++)
        {
            counter++;
        }
        Thread.Sleep(1); // Small delay to increase race condition chance
    }
 
    // Method to decrement the counter in ThreadPool
    static void DecrementCounter(object state)
    {
        // WITHOUT LOCK (Race condition can occur)
        for (int i = 0; i < 100; i++)
        {
            counter--;
        }
        Thread.Sleep(1); // Small delay to increase race condition chance
    }
}

الناتج المتوقع: 0 (200 عملية زيادة - 200 عملية إنقاص)
الناتج الفعلي: يتفاوت (مثل -23، 15، 42) بسبب حالات التسابق

منع حالات التسابق باستخدام الأقفال (Locks)

static void IncrementCounter(object state)
{
    // WITH LOCK (No race condition)
    lock (lockObject)
    {
        for (int i = 0; i < 100; i++)
        {
            counter++;
        }
        Thread.Sleep(1);
    }
}
 
static void DecrementCounter(object state)
{
    // WITH LOCK (No race condition)
    lock (lockObject)
    {
        for (int i = 0; i < 100; i++)
        {
            counter--;
        }
        Thread.Sleep(1);
    }
}

كيف تعمل الأقفال (Locks)

sequenceDiagram
    participant T1 as Thread 1
    participant Lock as Lock Object
    participant T2 as Thread 2
    participant Data as Shared Data
    
    T1->>Lock: Acquire lock
    Lock-->>T1: Lock granted
    T2->>Lock: Try acquire lock
    Lock-->>T2: Wait...
    T1->>Data: Read/Write
    T1->>Lock: Release lock
    Lock-->>T2: Lock granted
    T2->>Data: Read/Write
    T2->>Lock: Release lock

المفاهيم الأساسية:

  • تضمن عبارة lock أن thread واحدة فقط تُنفِّذ القسم الحرج (Critical Section) في كل مرة
  • تنتظر باقي الـ threads حتى يُحرَّر القفل
  • اقفل دائمًا على كائن (Object) مخصص لذلك (private static object lockObject)
  • لا تقفل أبدًا على this أو الأنواع (Types) أو السلاسل النصية (Strings)

الـ Thread Pool

ما هو الـ ThreadPool؟

الـ ThreadPool هو مجموعة من الـ threads تم إنشاؤها مسبقًا وتُديرها بيئة تشغيل .NET. بدلًا من إنشاء threads جديدة لكل مهمة، يمكنك وضع عناصر العمل في طابور (Queue) خاص بالـ ThreadPool.

graph TB
    A[Application] -->|QueueUserWorkItem| B[ThreadPool]
    B --> C[Thread 1]
    B --> D[Thread 2]
    B --> E[Thread 3]
    B --> F[Thread N]
    C -->|Executes| G[Task Queue]
    D -->|Executes| G
    E -->|Executes| G
    F -->|Executes| G
    
    style B fill:#e1f5ff
    style G fill:#fff4e1

مزايا الـ ThreadPool

  1. الكفاءة: يعيد استخدام threads موجودة بدلًا من إنشاء جديدة
  2. إدارة الموارد: يحدد أقصى عدد من الـ threads المتزامنة
  3. الأداء: يُقلِّل من العبء الناتج عن إنشاء الـ threads وإنهائها
  4. الإدارة التلقائية: تتولى بيئة تشغيل .NET إدارة دورة حياة الـ threads

الـ ThreadPool مقابل الـ Threads اليدوية

الميزةالـ ThreadPoolالـ Threads اليدوية
عبء الإنشاءمنخفض (يعيد استخدام الـ threads)مرتفع (ينشئ threads جديدة)
التحكممحدودتحكم كامل
الأنسب لـالمهام القصيرةالعمليات طويلة الأمد
التنظيف (Cleanup)تلقائييدوي

استخدام الـ ThreadPool

// Set maximum threads (optional)
ThreadPool.SetMaxThreads(50, 50);
 
// Queue work items
ThreadPool.QueueUserWorkItem(IncrementCounter);
ThreadPool.QueueUserWorkItem(DecrementCounter);

تحليل الأداء: أحادي مقابل متعدد الـ Threads

مثال مقارن

يقارن هذا المثال بين التنفيذ الأحادي والمتعدد الـ threads لجمع مليار رقم.

using System;
using System.Diagnostics;
using System.Threading;
 
class Program
{
    static long totalSum = 0; // Shared variable to hold the total sum
    static object lockObject = new object(); // Lock object to prevent race conditions
 
    static void Main()
    {
        Console.WriteLine("Single-threaded process started.");
 
        // Start timing
        Stopwatch stopwatch = new Stopwatch();
        stopwatch.Start();
 
        // Perform the task: Sum one billion numbers
        CalculatePartialSum(1, 1_000_000_000);
 
        // Stop timing
        stopwatch.Stop();
 
        Console.WriteLine($"Single-threaded sum: {totalSum}");
        Console.WriteLine($"Time taken (single-threaded): {stopwatch.ElapsedMilliseconds} ms");
 
        // -----------------------------------------------------
        Console.WriteLine("\nMulti-threaded process started.");
        totalSum = 0;
        
        // Start timing
        stopwatch.Reset();
        stopwatch.Start();
        
        // Define ranges for each thread
        Thread thread1 = new Thread(() => CalculatePartialSum(1, 250_000_000));
        Thread thread2 = new Thread(() => CalculatePartialSum(250_000_001, 500_000_000));
        Thread thread3 = new Thread(() => CalculatePartialSum(500_000_001, 750_000_000));
        Thread thread4 = new Thread(() => CalculatePartialSum(750_000_001, 1_000_000_000));
        
        // Start all threads
        thread1.Start();
        thread2.Start();
        thread3.Start();
        thread4.Start();
        
        // Wait for all threads to complete
        thread1.Join();
        thread2.Join();
        thread3.Join();
        thread4.Join();
        
        // Stop timing
        stopwatch.Stop();
        
        Console.WriteLine($"Multi-threaded sum: {totalSum}");
        Console.WriteLine($"Time taken (multi-threaded): {stopwatch.ElapsedMilliseconds} ms");
    }
 
    // Method to calculate partial sum within a range
    static void CalculatePartialSum(int start, int end)
    {
        // Calculate partial sum locally (no contention)
        long partialSum = 0;
        for (int i = start; i <= end; i++)
        {
            partialSum += i;
        }
        
        // Lock only when updating shared variable
        lock (lockObject)
        {
            totalSum += partialSum;
        }
    }
}

مخطط سير التنفيذ

flowchart TB
    subgraph Single-Threaded
    A1[Start] --> A2[Calculate 1 to 1B]
    A2 --> A3[End]
    end
    
    subgraph Multi-Threaded
    B1[Start] --> B2[Thread 1: 1-250M]
    B1 --> B3[Thread 2: 250M-500M]
    B1 --> B4[Thread 3: 500M-750M]
    B1 --> B5[Thread 4: 750M-1B]
    B2 --> B6[Join All]
    B3 --> B6
    B4 --> B6
    B5 --> B6
    B6 --> B7[End]
    end
    
    style A2 fill:#ffcccc
    style B2 fill:#ccffcc
    style B3 fill:#ccffcc
    style B4 fill:#ccffcc
    style B5 fill:#ccffcc

استراتيجية التحسين: المجموع الجزئي (Partial Sum)

flowchart LR
    A[Thread 1] -->|Calculate locally| B[partialSum1]
    C[Thread 2] -->|Calculate locally| D[partialSum2]
    E[Thread 3] -->|Calculate locally| F[partialSum3]
    G[Thread 4] -->|Calculate locally| H[partialSum4]
    
    B -->|Lock once| I[totalSum]
    D -->|Lock once| I
    F -->|Lock once| I
    H -->|Lock once| I
    
    style B fill:#e8f5e9
    style D fill:#e8f5e9
    style F fill:#e8f5e9
    style H fill:#e8f5e9
    style I fill:#fff4e1

ثلاثة أساليب:

  1. بدون قفل (خاطئ):
// ❌ Race condition - incorrect result
for (int i = start; i <= end; i++)
{
    totalSum += i;
}
  1. مع قفل في كل تكرار (بطيء):
// ✓ Correct but very slow - excessive locking
for (int i = start; i <= end; i++)
{
    lock (lockObject)
    {
        totalSum += i;
    }
}
  1. مجموع جزئي مع قفل (الأمثل):
// ✓✓ Correct and fast - minimal locking
long partialSum = 0;
for (int i = start; i <= end; i++)
{
    partialSum += i;
}
lock (lockObject)
{
    totalSum += partialSum;
}

نتائج الأداء

نتائج نموذجية على معالج رباعي النواة (4-core CPU):

الأسلوبالزمنمعدل التسريع
أحادي الـ thread4000 ms1x
متعدد الـ threads (4 threads)1200 ms3.3x

ملاحظات مهمة:

  • يوفر تعدد الـ threads تسريعًا ملحوظًا في المهام المرتبطة بالمعالج (CPU-bound)
  • التسريع ليس خطيًا بسبب العبء الإضافي والمزامنة
  • استراتيجية القفل الصحيحة أساسية للأداء
  • أفضل أداء: تقليل التنافس على القفل (Lock Contention) عبر تقليل تكرار القفل

أمثلة تطبيقية

المثال 1: تسمية الـ Thread

private static void MainThreadName()
{
    // Get the current thread
    Thread currentThread = Thread.CurrentThread;
    
    // Get the current thread's name
    string currentThreadName = currentThread.Name ?? "No name";
    Console.WriteLine($"Current thread name: {currentThreadName}");
    
    // Set the current thread's name
    currentThread.Name = "Main Thread";
    Console.WriteLine($"Current thread name: {currentThread.Name}");
}

المثال 2: تعبيرات Lambda مع الـ Threads

// Using lambda expressions to pass parameters
Thread thread1 = new Thread(() => CalculatePartialSum(1, 250_000_000));

المثال 3: حالات الـ Thread

Thread myThread = new Thread(DoWork);
 
Console.WriteLine(myThread.ThreadState); // Unstarted
myThread.Start();
Console.WriteLine(myThread.ThreadState); // Running
myThread.Join();
Console.WriteLine(myThread.ThreadState); // Stopped

أفضل الممارسات

افعل ✓

  1. استخدم الـ ThreadPool للمهام القصيرة: فعّال للعمليات الصغيرة الكثيرة
  2. قلِّل مدة القفل: اقفل الأقسام الحرجة فقط
  3. استخدم متغيرات محلية: قلِّل الحالة المشتركة لتقليل التنافس
  4. اجعل الـ threads في الخلفية عند الحاجة: امنع إعاقة إغلاق التطبيق
  5. تعامل مع الاستثناءات: ضع كود الـ thread داخل كتل try-catch
  6. استخدم Join() بحذر: تجنَّب حالات الجمود (Deadlocks) الناتجة عن الانتظار الدائري

لا تفعل ✗

  1. لا تستخدم Abort(): مُهمَل وخطير
  2. لا تقفل على كائنات عامة (Public): استخدم كائنات قفل خاصة (Private)
  3. لا تقفل على الأنواع (Types) أو السلاسل النصية (Strings): قد يسبب حالات جمود
  4. لا تُنشئ عددًا كبيرًا جدًا من الـ threads: يسبب عبئًا إضافيًا واستنزافًا للموارد
  5. لا تتجاهل حالات التسابق: احمِ البيانات المشتركة دائمًا
  6. لا تنسَ تحرير الموارد (Dispose): نظِّف الموارد بشكل صحيح

ملخص

المفاهيم الأساسية التي تم تناولها

mindmap
  root((Threading))
    Basics
      Thread Creation
      Start and Join
      Thread Lifecycle
    Synchronization
      Race Conditions
      Locks
      Critical Sections
    ThreadPool
      Queuing Work
      Resource Management
      Efficiency
    Performance
      Single vs Multi
      Partial Sum Strategy
      Lock Optimization

مخطط اتخاذ القرار في الـ Threading

flowchart TD
    A[Need Concurrency?] -->|No| B[Sequential Execution]
    A -->|Yes| C{Short Tasks?}
    C -->|Yes| D[Use ThreadPool]
    C -->|No| E[Create Manual Threads]
    E --> F{Shared Data?}
    F -->|No| G[No Synchronization Needed]
    F -->|Yes| H[Use Locks/Synchronization]
    H --> I[Minimize Lock Duration]
    I --> J[Use Partial Results]
    
    style D fill:#e8f5e9
    style E fill:#fff4e1
    style H fill:#ffebee

تمارين المعمل

تمرين 1: الـ Threading الأساسي

أنشئ برنامجًا يحتوي على thread تطبعان الأرقام الزوجية والفردية في آن واحد من 1 إلى 100.

تمرين 2: حالة التسابق

عدِّل مثال الـ ThreadPool ليقوم بما يلي:

  1. شغِّله أولًا بدون أقفال ولاحظ النتائج الخاطئة
  2. أضِف أقفالًا وتحقق من صحة النتائج
  3. قارن أزمنة التنفيذ

تمرين 3: تحليل الأداء

أنشئ برنامجًا يقوم بما يلي:

  1. ضرب مصفوفتين كبيرتين (1000×1000)
  2. تنفيذ نسختين، أحادية الـ thread ومتعددة الـ threads
  3. مقارنة أزمنة التنفيذ
  4. تحليل معدل التسريع مقابل عدد الـ threads (1، 2، 4، 8)

تمرين 4: نمط المنتِج والمستهلِك (Producer-Consumer)

نفِّذ نمط منتِج-مستهلِك بسيطًا باستخدام الـ threads والأقفال.


مراجع ومصادر إضافية للقراءة

  • توثيق Microsoft الرسمي للـ Threading في .NET
  • "C# Threading Handbook" - أفضل الممارسات للبرمجة المتزامنة
  • Operating System Concepts (Silberschatz) - فصل الـ Threads
  • فهم بدائيات مزامنة الـ threads (Thread Synchronization Primitives)
  • تحسين الأداء في التطبيقات متعددة الـ threads

أسئلة للتفكير

  1. متى ينبغي استخدام تعدد الـ threads مقابل الـ thread الواحدة؟
  2. ما المقايضات (Trade-offs) بين الـ ThreadPool وإنشاء الـ threads يدويًا؟
  3. كيف تحدث حالات التسابق وكيف يمكن منعها؟
  4. لماذا يكون أسلوب المجموع الجزئي أسرع من القفل في كل تكرار؟
  5. ماذا يحدث إذا نسيت استدعاء Join() على thread في الواجهة (Foreground)؟
  6. كيف يؤثر عدد أنوية المعالج على أداء تعدد الـ threads؟

نهاية وثيقة المعمل