قوائم المهام في C# (.NET): System.Threading.Channels و TPL Dataflow

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

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

ابدأ الدورة

قوائم المهام لـ C# (.NET): System.Threading.Channels و TPL Dataflow

عند تطوير تطبيقات متعددة الخيوط في C# (.NET)، غالبًا ما تنشأ الحاجة لتنظيم نقل البيانات غير المتزامن بين المنتجين (producers) والمستهلكين (consumers). يتم حل النمط الكلاسيكي Producer-Consumer باستخدام قوائم المهام. في بيئة .NET، هناك أداتان قويتان لذلك: System.Threading.Channels (ظهر في .NET Core 3.0) ومكتبة TPL Dataflow (جزء من .NET منذ 4.5).

في هذه المقالة، سنشرح كلا النهجين، خصائصهما، ونقدم أمثلة عملية للكود.



1. System.Threading.Channels — قائمة خفيفة وسريعة

مساحة الأسماء System.Threading.Channels توفر قوائم آمنة للخيوط وغير متزامنة، محسّنة لسيناريوهات الإنتاجية العالية. الأنواع الرئيسية:

  • Channel<T> — فئة مجردة للقناة.
  • Channel.CreateUnbounded<T>() — قائمة غير محدودة (خطر تجاوز الذاكرة).
  • Channel.CreateBounded<T>(int capacity) — قائمة محدودة مع التحكم في الضغط.
  • ChannelWriter<T> — واجهة للكتابة.
  • ChannelReader<T> — واجهة للقراءة.


مثال: Producer-Consumer مع Channel

لننشئ مثالًا بسيطًا، حيث يولد منتج واحد أرقامًا، ويقوم مستهلك واحد بمعالجتها.

using System.Threading.Channels;using System.Threading.Tasks;

var channel = Channel.CreateUnbounded<int>();

// Producervar producer = Task.Run(async () =>{ for (int i = 0; i < 10; i++) { await channel.Writer.WriteAsync(i); Console.WriteLine($"تم الإنتاج: {i}"); await Task.Delay(100); } channel.Writer.Complete(); // إشارة الانتهاء});

// Consumervar consumer = Task.Run(async () =>{ await foreach (var item in channel.Reader.ReadAllAsync()) { Console.WriteLine($"تمت المعالجة: {item * 2}"); await Task.Delay(200); }});

await Task.WhenAll(producer, consumer);Console.WriteLine("تم!");

مزايا Channels:

  • تكاليف إضافية ضئيلة (بدون تخصيص كائنات إضافية).
  • دعم مدمج لـ async/await و IAsyncEnumerable.
  • مثالي للأنظمة عالية التحميل (مثل معالجة حزم الشبكة).


2. TPL Dataflow — خط أنابيب معالجة قوي

مكتبة System.Threading.Tasks.Dataflow توفر كتلًا أكثر تعقيدًا لبناء خطوط الأنابيب (pipeline) وشبكات معالجة البيانات. الكتل الرئيسية:

  • BufferBlock<T> — قائمة بسيطة.
  • ActionBlock<T> — كتلة تنفذ إجراءً على كل عنصر.
  • TransformBlock<TInput, TOutput> — تحويل كل عنصر.
  • BatchBlock<T> — تجميع العناصر في دفعات.


مثال: خط أنابيب مع ActionBlock

لننفذ نفس المهمة: منتج يرسل أرقامًا، ومستهلك يعالجها.

using System.Threading.Tasks.Dataflow;

var actionBlock = new ActionBlock<int>(async item =>{ await Task.Delay(200); Console.WriteLine($"تمت المعالجة: {item * 2}");}, new ExecutionDataflowBlockOptions { MaxDegreeOfParallelism = 2 });

// Producerfor (int i = 0; i < 10; i++){ await actionBlock.SendAsync(i); Console.WriteLine($"تم الإنتاج: {i}"); await Task.Delay(100);}

actionBlock.Complete();await actionBlock.Completion;Console.WriteLine("تم!");

مزايا TPL Dataflow:

  • مرونة عالية في الإعداد

توصيات