C++ Aufgabenplaner: Vollständige Anleitung
Ein Aufgabenplaner (Task Scheduler) ist ein leistungsstarkes Werkzeug, mit dem Sie die Ausführung von Code zu bestimmten Zeitpunkten oder in festgelegten Intervallen automatisieren können. In C++ erfordert die Implementierung eines solchen Planers ein Verständnis von Multithreading, Synchronisation und der Arbeit mit Zeitintervallen. In diesem Artikel werden wir erläutern, wie Sie mit der Standard-C++-Bibliothek (C++11 und neuer) einen eigenen Aufgabenplaner von Grund auf erstellen.
Grundlegende Komponenten des Aufgabenplaners
Jeder Aufgabenplaner besteht aus mehreren Schlüsselelementen:
- Aufgabenspeicher — eine Warteschlange oder ein Container zum Speichern geplanter Aufgaben.
- Timer — ein Mechanismus, der bestimmt, wann eine Aufgabe ausgeführt werden soll.
- Thread-Pool — für die parallele Ausführung von Aufgaben ohne Blockierung des Hauptthreads.
- Abbruchmechanismus — die Möglichkeit, eine Aufgabe vor ihrer Ausführung abzubrechen.
Einfache Implementierung eines Aufgabenplaners mit einem Thread
Beginnen wir mit einem grundlegenden Beispiel, bei dem Aufgaben nacheinander in einem Hintergrundthread ausgeführt werden:
#include <iostream>#include <thread>#include <chrono>#include <functional>#include <queue>#include <mutex>#include <condition_variable>
class TaskScheduler {public: // Plant eine Aufgabe mit einer Verzögerung void schedule(std::function<void()> task, int delay_ms) { std::lock_guard<std::mutex> lock(mutex_); tasks_.push({std::chrono::steady_clock::now() + std::chrono::milliseconds(delay_ms), task}); cv_.notify_one(); }
// Startet den Planer in einem separaten Thread void start() { worker_ = std::thread([this]() { while (!stop_) { std::unique_lock<std::mutex> lock(mutex_); if (tasks_.empty()) { cv_.wait(lock, [this]() { return !tasks_.empty() || stop_; }); } if (stop_) break;
auto now = std::chrono::steady_clock::now(); auto& top = tasks_.top(); if (top.time <= now) { auto task = top.task; tasks_.pop(); lock.unlock(); task(); // Führt die Aufgabe aus } else { cv_.wait_until(lock, top.time); } } }); }
void stop() { stop_ = true; cv_.notify_all(); if (worker_.joinable()) worker_.join(); }
private: struct Task { std::chrono::steady_clock::time_point time; std::function<void()> task; bool operator>(const Task& other) const { return time > other.time; } };
std::priority_queue<Task, std::vector<Task>, std::greater<Task>> tasks_; std::mutex mutex_; std::condition_variable cv_; std::thread worker_; bool stop_ = false;};
int main() { TaskScheduler scheduler; scheduler.start();
scheduler.schedule([]() { std::cout << "Aufgabe 1 nach 2 Sekunden ausgeführt"; }, 2000);
scheduler.schedule([]() { std::cout << "Aufgabe 2 nach 1 Sekunde ausgeführt"; }, 1000);
std::this_thread::sleep_for(std::chrono::seconds(3)); scheduler.stop(); return 0;}Planer mit Thread-Pool für parallele Ausführung
Für komplexere Szenarien, in denen Aufgaben lange ausgeführt werden können, ist es besser, einen Thread-Pool zu verwenden. Hier ist ein Beispiel mit einem minimalen Pool:
#include <vector>#include <queue>#include <thread>#include <mutex>#include <condition_variable>#include <f