Planejador de Tarefas em C++: Guia Completo
O Planejador de Tarefas (Task Scheduler) é uma ferramenta poderosa que permite automatizar a execução de código em momentos específicos ou com uma periodicidade definida. Em C++, a implementação de tal planejador requer compreensão de multithreading, sincronização e trabalho com intervalos de tempo. Neste artigo, vamos analisar como criar seu próprio planejador de tarefas do zero, usando a biblioteca padrão C++ (C++11 e posteriores).
Componentes Principais do Planejador
Qualquer planejador de tarefas consiste em vários elementos-chave:
- Armazenamento de tarefas — uma fila ou contêiner para armazenar tarefas agendadas.
- Temporizador — um mecanismo que determina quando uma tarefa deve ser executada.
- Pool de threads — para execução paralela de tarefas sem bloquear a thread principal.
- Mecanismo de cancelamento — a capacidade de cancelar uma tarefa antes de sua execução.
Implementação Simples de um Planejador com uma Única Thread
Vamos começar com um exemplo básico, onde as tarefas são executadas sequencialmente em uma thread em segundo plano:
#include <iostream>#include <thread>#include <chrono>#include <functional>#include <queue>#include <mutex>#include <condition_variable>
class TaskScheduler {public: // Agenda uma tarefa com atraso 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(); }
// Inicia o planejador em uma thread separada 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(); // Executa a tarefa } 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 << "Tarefa 1 executada após 2 segundos"; }, 2000);
scheduler.schedule([]() { std::cout << "Tarefa 2 executada após 1 segundo"; }, 1000);
std::this_thread::sleep_for(std::chrono::seconds(3)); scheduler.stop(); return 0;}Planejador com Pool de Threads para Execução Paralela
Para cenários mais complexos, onde as tarefas podem demorar muito para serem executadas, é melhor usar um pool de threads. Aqui está um exemplo com um pool mínimo:
#include <vector>#include <queue>#include <thread>#include <mutex>#include <condition_variable>#include <f