Arbeiten mit Aufgabenwarteschlangen in C#: für Anfänger
\n\nIn der modernen Entwicklung tritt häufig die Notwendigkeit auf, viele Aufgaben zu verarbeiten, ohne den Hauptthread der Anwendung zu blockieren. Stellen Sie sich einen Webserver vor, der gleichzeitig Hunderten von Benutzern antworten muss, oder einen Hintergrunddienst, der Dateiuploads verarbeitet. In solchen Situationen hilft die Aufgabenwarteschlange (Task Queue) – eine der grundlegenden Datenstrukturen und Programmiermuster.
\n\nIn diesem Artikel werden wir anhand praktischer Beispiele analysieren, wie Aufgabenwarteschlangen in C# (.NET) funktionieren, angefangen bei den grundlegenden Klassen bis hin zu threadsicheren Lösungen für Multithread-Anwendungen.
\n\nWas ist eine Aufgabenwarteschlange und warum wird sie benötigt?
\n\nEine Warteschlange (Queue) ist eine Datenstruktur, die nach dem FIFO-Prinzip (First In, First Out) funktioniert: Das zuerst hinzugefügte Element wird zuerst verarbeitet. Im Kontext von Aufgaben bedeutet dies, dass wir eine Aufgabe in die Warteschlange stellen und später verarbeiten können, wenn Ressourcen frei werden.
\n\nHauptanwendungsszenarien für Aufgabenwarteschlangen:
\n- \n
- Hintergrundverarbeitung (E-Mail-Versand, Berichtserstellung) \n
- Lastverteilung (Verteilung von Aufgaben auf Arbeitsthreads) \n
- Anfragepufferung (wenn das System nicht alle Anfragen sofort verarbeiten kann) \n
- Asynchrones Logging (Protokollierung ohne Verlangsamung der Hauptanwendung) \n
Basisklasse Queue<T> in C#
\n\nIn .NET gibt es die integrierte Klasse Queue<T> aus dem Namespace System.Collections.Generic. Sehen wir uns ein einfaches Beispiel an:
using System;\nusing System.Collections.Generic;\n\npublic class SimpleQueueExample\n{\n public static void Main()\n {\n // Erstellen einer Warteschlange für Zeichenfolgen\n Queue<string> taskQueue = new Queue<string>();\n\n // Aufgaben hinzufügen\n taskQueue.Enqueue("E-Mail an Kunden senden");\n taskQueue.Enqueue("Bericht erstellen");\n taskQueue.Enqueue("Cache aktualisieren");\n\n Console.WriteLine($"Anzahl der Aufgaben in der Warteschlange: {taskQueue.Count}");\n\n // Aufgaben der Reihe nach verarbeiten\n while (taskQueue.Count > 0)\n {\n string task = taskQueue.Dequeue();\n Console.WriteLine($"Verarbeitung: {task}");\n }\n\n Console.WriteLine($"Warteschlange ist leer: {taskQueue.Count == 0}");\n }\n}\n\nWichtige Methoden von Queue<T>:
- \n
Enqueue(T item)– fügt ein Element am Ende der Warteschlange hinzu \n Dequeue()– entfernt und gibt das Element vom Anfang der Warteschlange zurück \n Peek()– gibt das erste Element zurück, ohne es zu entfernen \n Count– Anzahl der Elemente in der Warteschlange \n
Wichtig: Queue<T> ist nicht threadsicher. Wenn mehrere Threads gleichzeitig in die Warteschlange schreiben und daraus lesen, treten Fehler auf. Verwenden Sie für Multithread-Szenarien ConcurrentQueue<T>.
Threadsichere Warteschlange: ConcurrentQueue<T>
\n\nIn realen Anwendungen werden Aufgabenwarteschlangen fast immer im Multithread-Kontext verwendet. Dafür gibt es in .NET ConcurrentQueue<T> aus dem Namespace System.Collections.Concurrent.
using System;\nusing System.Collections.Concurrent;\nusing System.Threading.Tasks;\n\npublic class ConcurrentQueueExample\n{\n public static void Main()\n {\n ConcurrentQueue<string> taskQueue = new ConcurrentQueue<string>();\n\n // Aufgabe 1: Elemente hinzufügen (Producer)\n Task producer = Task.Run(() =>\n {\n for (int i = 1; i <= 10; i++)\n {\n taskQueue.Enqueue($"Aufgabe #{i}");\n Console.WriteLine($"Aufgabe #{i} hinzugefügt");\n