Arbeit mit Aufgabenwarteschlangen in C#: Vollständiger Leitfaden

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einführung in Aufgabenwarteschlangen in C#



In der modernen Multithread-Programmierung ist die Aufgabenwarteschlange (task queue) ein fundamentales Muster zur Organisation asynchroner Datenverarbeitung. In C# (.NET) wird dieses Muster durch mehrere leistungsstarke Mechanismen implementiert, die es Entwicklern ermöglichen, Ausführungsthreads effizient zu verwalten und die Last zu verteilen.



Die Aufgabenwarteschlange löst das klassische Producer-Consumer-Problem (Erzeuger-Verbraucher), bei dem ein Teil des Codes Aufgaben erstellt und ein anderer sie verarbeitet. In diesem Artikel betrachten wir die wichtigsten Methoden zur Implementierung von Aufgabenwarteschlangen in C#: von einfachen Sammlungen bis hin zu modernen Kanälen (Channel).



Grundlegende Ansätze zur Implementierung von Aufgabenwarteschlangen



1. ConcurrentQueue — Threadsichere Warteschlange



Die einfachste und grundlegendste Implementierung ist ConcurrentQueue<T> aus dem Namespace System.Collections.Concurrent. Sie bietet die threadsicheren Methoden Enqueue und 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(); } } }}


Vorteile:

  • Einfachheit der Nutzung
  • Geringer Speicherverbrauch
  • Threadsicherheit ohne Sperren


Nachteile:

  • Aktives Warten (busy waiting) in der Schleife
  • Fehlende Signalisierung neuer Aufgaben
  • Schwierigkeit beim Stoppen


2. BlockingCollection — Warteschlange mit Blockierung



BlockingCollection<T> löst das Problem des aktiven Wartens. Sie bietet die Methoden Add und Take, die den Thread blockieren, bis Daten verfügbar sind.



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(); }}


Wesentliche Merkmale:

  • Automatische Blockierung des Threads bei fehlenden Aufgaben
  • Unterstützung für Abbruch durch CancellationToken
  • Möglichkeit zur Begrenzung der Warteschlangenkapazität
  • Sicheres Beenden durch CompleteAdding


Moderner Ansatz: Channel<T>



Seit .NET Core 3.0 und .NET 5 gibt es in C# System.Threading.Channels.Channel<T> — dies ist die leistungsfähigste und flexibelste Implementierung einer Aufgabenwarteschlange. Sie unterstützt asynchrone Operationen, Abbruch und Begrenzung des Durchsatzes

Blogs

Buchempfehlungen