Aufgabenwarteschlangen in C++: Ein praktischer Leitfaden mit Beispielen

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

Einleitung: Warum werden Aufgabenwarteschlangen in C++ benötigt?

\n\n

In der modernen Programmierung, insbesondere bei der Arbeit mit Multithread-Anwendungen, entsteht häufig die Notwendigkeit, die Ausführung von Aufgaben in der Reihenfolge ihres Eingangs zu organisieren. Aufgabenwarteschlangen (Task Queues) sind ein grundlegendes Muster, das es ermöglicht, Arbeit effizient auf Threads zu verteilen, Anfragen von Webservern zu verarbeiten, Hintergrundprozesse zu verwalten und Race Conditions zu vermeiden.

\n\n

In C++ bietet die Standardbibliothek den leistungsstarken Container std::queue, der sich ideal für die Implementierung von Aufgabenwarteschlangen eignet. In der Praxis reicht es jedoch nicht aus, Elemente nur zu speichern; sie müssen sicher zwischen Threads übertragen, Fehler behandelt und der Lebenszyklus von Aufgaben verwaltet werden. In diesem Artikel analysieren wir, wie man Aufgabenwarteschlangen in C++ von Grund auf erstellt, unter Verwendung moderner Sprachfunktionen (C++11 und neuer).

\n\n

Wir werden behandeln:

\n
    \n
  • Grundlegende Arbeit mit std::queue
  • \n
  • Threadsichere Aufgabenwarteschlange
  • \n
  • Implementierung eines Thread Pools
  • \n
  • Prioritätswarteschlangen
  • \n
\n\n

1. Grundlegende Aufgabenwarteschlange mit std::queue

\n\n

Beginnen wir mit einem einfachen Beispiel. std::queue ist ein Container-Adapter, der nach dem FIFO-Prinzip (First In, First Out) funktioniert. Er stellt die Methoden push(), pop(), front() und empty() bereit.

\n\n

So kann eine Aufgabenwarteschlange organisiert werden, bei der jede Aufgabe eine Funktion (oder ein aufrufbares Objekt) ist:

\n\n
#include <iostream>\n#include <queue>\n#include <functional>\n\nint main() {\n    // Aufgabenwarteschlange, bei der jede Aufgabe ein std::function<void()> ist\n    std::queue<std::function<void()>> taskQueue;\n\n    // Aufgaben hinzufügen\n    taskQueue.push([]() { std::cout << "Aufgabe 1 ausgeführt\\n"; });\n    taskQueue.push([]() { std::cout << "Aufgabe 2 ausgeführt\\n"; });\n    taskQueue.push([]() { std::cout << "Aufgabe 3 ausgeführt\\n"; });\n\n    // Warteschlange verarbeiten\n    while (!taskQueue.empty()) {\n        auto task = taskQueue.front();\n        task();  // Aufgabe ausführen\n        taskQueue.pop();  // Ausgeführte Aufgabe entfernen\n    }\n\n    return 0;\n}
\n\n

Dieser Code funktioniert, jedoch nur im Singlethread-Modus. Wenn mehrere Threads gleichzeitig Aufgaben hinzufügen oder entnehmen, entsteht eine Race Condition. Für Multithreading ist eine Synchronisation erforderlich.

\n\n

2. Threadsichere Aufgabenwarteschlange

\n\n

Um die Warteschlange für den Multithread-Zugriff sicher zu machen, verwenden wir einen Mutex (std::mutex) und eine Bedingungsvariable (std::condition_variable). Dies ist das klassische Producer-Consumer-Muster (Erzeuger-Verbraucher).

\n\n

Implementierung einer threadsicheren Warteschlange:

\n\n
#include <queue>\n#include <mutex>\n#include <condition_variable>\n#include <functional>\n#include <thread>\n\nclass ThreadSafeQueue {\nprivate:\n    std::queue<std::function<void()>> queue_;\n    std::mutex mutex_;\n    std::condition_variable cond_;\n    bool stop_ = false;\n\npublic:\n    void push(std::function<void()> task) {\n        std::lock_guard<std::mutex> lock(mutex_);\n        queue_.push(task);\n        cond_.notify_one();  // Einen wartenden Thread benachrichtigen\n    }\n\n    std::function<void()> pop() {\n        std::unique_lock<std::mutex> lock(mutex_);\n        cond_.wait(lock, [this]() { return !queue_.empty() || stop_; });\n        if (stop_ && queue_.empty()) {\n            return nullptr;  // Stoppsignal\n        }\n        auto task = queue_.front();\n        queue_.pop();\n        return task;\n    }\n\n    void stop() {\n        std::lock_guard<std::mutex> lock(mutex_);\n        stop_ = true;\n        cond_.notify_all();  // Alle Threads zum Beenden aufwecken\n    }\n};
\n\n

Wichtige Punkte:

\n
    \n
  • std::lock_gu

Blogs

Buchempfehlungen