Aufgabenplaner in C++: Task Scheduler von Grund auf erstellen

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

Aufgabenplaner in C++: Architektur, Implementierung und Beispiele



Ein Aufgabenplaner (Task Scheduler) ist eine der Schlüsselkomponenten moderner hochbelasteter Systeme, Spiele-Engines, Anwendungsserver und Embedded-Geräte. In diesem Artikel werden wir im Detail besprechen, wie Sie mit der Standardbibliothek der Sprache einen eigenen Aufgabenplaner in C++ von Grund auf erstellen. Sie erfahren, wie eine Aufgabenwarteschlange aufgebaut ist, wie Timer funktionieren, wie Sie Threads synchronisieren und typische Fehler vermeiden.



Wozu braucht man einen Aufgabenplaner?

Ein Aufgabenplaner ermöglicht:

  • asynchrone Ausführung von Code in Hintergrund-Threads;
  • Festlegen von Verzögerungen und periodischen Ausführungen;
  • Verwaltung von Prioritäten und Ausführungsreihenfolge;
  • Optimierung der CPU-Ressourcennutzung.

Im Gegensatz zu einem einfachen std::thread bietet der Planer eine flexible Kontrolle über die Ausführung und vermeidet die Erstellung von Threads für jede einzelne Aufgabe.



Basisarchitektur

Jeder Planer besteht aus drei Hauptteilen:

  • Aufgabenwarteschlange — speichert Aufgabenobjekte (Funktionen, Argumente, Metadaten);
  • Thread-Pool — ein oder mehrere Arbeits-Threads, die Aufgaben aus der Warteschlange abrufen und ausführen;
  • Timer / Zeitplaner — ermöglicht das Verschieben der Ausführung oder das Starten von Aufgaben nach einem Zeitplan.


Implementierung einer Aufgabenwarteschlange in C++

Zunächst definieren wir die Aufgabenstruktur:

struct Task {    std::function<void()> func;    std::chrono::steady_clock::time_point executeAt;    size_t id;};

Die Warteschlange muss threadsicher sein. Wir verwenden std::priority_queue (damit Aufgaben mit geringerer Ausführungszeit zuerst abgerufen werden) und std::mutex zur Synchronisation:

#include <queue>#include <mutex>#include <condition_variable>#include <functional>#include <chrono>#include <thread>#include <vector>

class TaskScheduler {public: explicit TaskScheduler(size_t threadCount = std::thread::hardware_concurrency()) { for (size_t i = 0; i < threadCount; ++i) { workers.emplace_back([this] { workerLoop(); }); } }

~TaskScheduler() { stop(); }

// Aufgabe mit Verzögerung hinzufügen template<typename F, typename... Args> void schedule(F&& f, Args&&... args, std::chrono::milliseconds delay = std::chrono::milliseconds(0)) { auto task = std::bind(std::forward<F>(f), std::forward<Args>(args)...); Task t{std::move(task), std::chrono::steady_clock::now() + delay, nextId++}; { std::lock_guard<std::mutex> lock(mutex); queue.push(std::move(t)); } cv.notify_one(); }

void stop() { { std::lock_guard<std::mutex> lock(mutex); stopping = true; } cv.notify_all(); for (auto& worker : workers) { if (worker.joinable()) worker.join(); } }

private: struct Task { std::function<void()> func; std::chrono::steady_clock::time_point executeAt; size_t id;

bool operator>(const Task& other) const { return executeAt > other.executeAt; } };

void workerLoop() { while (true) { std::unique_lock<std::mutex> lock(mutex); cv.wait(lock, [this] { return stopping || !queue.empty(); }); if (stopping && queue.empty()) return;

if (!queue.empty()) { auto top = queue.top(); if (std::chrono::steady_clock::now() >= top.executeAt) { queue.pop(); lock.unlock(); top.func

Empfehlungen