Planificateur de tâches en C++ : architecture, implémentation et exemples
Le planificateur de tâches (Task Scheduler) est l'un des composants clés des systèmes modernes à forte charge, des moteurs de jeu, des serveurs d'applications et des dispositifs embarqués. Dans cet article, nous allons détailler comment créer votre propre planificateur de tâches en C++ de zéro, en utilisant la bibliothèque standard du langage. Vous apprendrez comment fonctionne la file d'attente des tâches, comment les minuteurs fonctionnent, comment synchroniser les threads et éviter les erreurs typiques.
Pourquoi un planificateur de tâches ?
Un planificateur de tâches permet de :
- exécuter du code de manière asynchrone dans des threads d'arrière-plan ;
- définir des délais et des exécutions périodiques ;
- gérer les priorités et l'ordre d'exécution ;
- optimiser l'utilisation des ressources CPU.
Contrairement à un simple std::thread, le planificateur offre un contrôle flexible sur l'exécution et évite de créer un thread pour chaque tâche.
Architecture de base
Tout planificateur se compose de trois parties principales :
- File d'attente des tâches — stocke les objets de tâches (fonctions, arguments, métadonnées) ;
- Pool de threads — un ou plusieurs threads de travail qui extraient les tâches de la file et les exécutent ;
- Minuteur / planificateur temporel — permet de différer l'exécution ou de lancer des tâches selon un calendrier.
Implémentation d'une file d'attente de tâches en C++
Pour commencer, définissons la structure d'une tâche :
struct Task { std::function<void()> func; std::chrono::steady_clock::time_point executeAt; size_t id;};La file d'attente doit être thread-safe. Utilisons std::priority_queue (afin que les tâches avec le temps d'exécution le plus court soient extraites en premier) et std::mutex pour la 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(); }
// Ajouter une tâche avec un délai 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 Aussi dans la bibliothèque
Analyse HTML en C++ : Aperçu des bibliothèques Gumbo, MyHTML et htmlcxx
Validation des données en PHP : Guide complet des outils et bibliothèques