Planejador de Tarefas em C++: arquitetura, implementação e exemplos
O Planejador de Tarefas (Task Scheduler) é um dos componentes-chave de sistemas modernos de alta carga, motores de jogos, servidores de aplicações e dispositivos embarcados. Neste artigo, vamos analisar detalhadamente como criar seu próprio planejador de tarefas em C++ do zero, usando a biblioteca padrão da linguagem. Você aprenderá como funciona uma fila de tarefas, como os temporizadores operam, como sincronizar threads e evitar erros típicos.
Para que serve um planejador de tarefas?
O planejador de tarefas permite:
- executar código assincronamente em threads em segundo plano;
- definir atrasos e execuções periódicas;
- gerenciar prioridades e ordem de execução;
- otimizar o uso dos recursos da CPU.
Diferente de um simples std::thread, o planejador oferece controle flexível sobre a execução e evita a criação de threads para cada tarefa.
Arquitetura básica
Qualquer planejador consiste em três partes principais:
- Fila de tarefas — armazena objetos de tarefa (funções, argumentos, metadados);
- Pool de threads — uma ou mais threads de trabalho que retiram tarefas da fila e as executam;
- Temporizador / Agendador de tempo — permite adiar a execução ou executar tarefas conforme uma programação.
Implementação da fila de tarefas em C++
Para começar, vamos definir a estrutura da tarefa:
struct Task { std::function<void()> func; std::chrono::steady_clock::time_point executeAt; size_t id;};A fila deve ser thread-safe. Usaremos std::priority_queue (para que tarefas com menor tempo de execução sejam retiradas primeiro) e std::mutex para sincronização:
#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(); }
// Adicionar tarefa com atraso 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