مقدمة في قوائم المهام في C#
في البرمجة متعددة الخيوط الحديثة، تُعد قائمة المهام (task queue) نمطًا أساسيًا لتنظيم المعالجة غير المتزامنة للبيانات. في C# (.NET)، يتم تنفيذ هذا النمط من خلال عدة آليات قوية تتيح للمطورين إدارة تدفقات التنفيذ بكفاءة وتوزيع الحمل.
تحل قائمة المهام المشكلة الكلاسيكية producer-consumer (منتج-مستهلك)، حيث يقوم جزء من الكود بإنشاء المهام بينما يقوم جزء آخر بمعالجتها. في هذه المقالة، سنستعرض الطرق الأساسية لتنفيذ قوائم المهام في C#: من المجموعات البسيطة إلى القنوات الحديثة (Channel).
الطرق الأساسية لتنفيذ قوائم المهام
1. ConcurrentQueue — قائمة انتظار آمنة للخيوط
أبسط وأدنى تنفيذ هو ConcurrentQueue<T> من مساحة الأسماء System.Collections.Concurrent. يوفر طرقًا آمنة للخيوط مثل Enqueue و TryDequeue.
using System;using System.Collections.Concurrent;using System.Threading.Tasks;
public class SimpleTaskQueue{ private readonly ConcurrentQueue<Action> _tasks = new(); private readonly Task _worker;
public SimpleTaskQueue() { _worker = Task.Run(ProcessTasks); }
public void Enqueue(Action task) { _tasks.Enqueue(task); }
private void ProcessTasks() { while (true) { if (_tasks.TryDequeue(out var task)) { task(); } } }}المزايا:
- سهولة الاستخدام
- استهلاك منخفض للذاكرة
- أمان الخيوط بدون أقفال
العيوب:
- الانتظار النشط (busy waiting) في الحلقة
- عدم وجود إشارة للمهام الجديدة
- صعوبة الإيقاف
2. BlockingCollection — قائمة انتظار مع الحظر
يحل BlockingCollection<T> مشكلة الانتظار النشط. يوفر طرق Add و Take التي تحظر الخيط حتى تظهر البيانات.
using System;using System.Collections.Concurrent;using System.Threading.Tasks;
public class BlockingTaskQueue : IDisposable{ private readonly BlockingCollection<Action> _tasks = new(new ConcurrentQueue<Action>()); private readonly Task[] _workers; private bool _disposed;
public BlockingTaskQueue(int workerCount = 4) { _workers = new Task[workerCount]; for (int i = 0; i < workerCount; i++) { _workers[i] = Task.Run(ProcessTasks); } }
public void Enqueue(Action task) { _tasks.Add(task); }
private void ProcessTasks() { foreach (var task in _tasks.GetConsumingEnumerable()) { if (_disposed) break; task(); } }
public void CompleteAdding() { _tasks.CompleteAdding(); }
public void Dispose() { _disposed = true; _tasks.CompleteAdding(); Task.WaitAll(_workers); _tasks.Dispose(); }}الميزات الرئيسية:
- حظر تلقائي للخيط عند عدم وجود مهام
- دعم الإلغاء عبر
CancellationToken - إمكانية تحديد سعة قائمة الانتظار
- إنهاء آمن عبر
CompleteAdding
النهج الحديث: Channel<T>
بدءًا من .NET Core 3.0 و .NET 5، ظهر في C# System.Threading.Channels.Channel<T> — وهو التنفيذ الأكثر أداءً ومرونة لقائمة المهام. يدعم العمليات غير المتزامنة، والإلغاء، وتحديد معدل النقل.