Planificador de tareas en C++: creamos un Task Scheduler desde cero

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Planificador de tareas en C++: arquitectura, implementación y ejemplos



El planificador de tareas (Task Scheduler) es uno de los componentes clave de los sistemas modernos de alta carga, motores de juegos, servidores de aplicaciones y dispositivos embebidos. En este artículo, analizaremos en detalle cómo crear su propio planificador de tareas en C++ desde cero, utilizando la biblioteca estándar del lenguaje. Aprenderá cómo está organizada la cola de tareas, cómo funcionan los temporizadores, cómo sincronizar subprocesos y evitar errores típicos.



¿Para qué sirve un planificador de tareas?

El planificador de tareas permite:

  • ejecutar código de forma asíncrona en subprocesos en segundo plano;
  • establecer retrasos y ejecuciones periódicas;
  • gestionar prioridades y el orden de ejecución;
  • optimizar el uso de los recursos de la CPU.

A diferencia de un simple std::thread, el planificador proporciona un control flexible sobre la ejecución y evita la creación de subprocesos para cada tarea.



Arquitectura básica

Cualquier planificador consta de tres partes principales:

  • Cola de tareas — almacena objetos de tarea (funciones, argumentos, metadatos);
  • Grupo de subprocesos — uno o varios subprocesos de trabajo que extraen tareas de la cola y las ejecutan;
  • Temporizador / planificador de tiempo — permite retrasar la ejecución o ejecutar tareas según un horario.


Implementación de la cola de tareas en C++

Para empezar, definamos la estructura de la tarea:

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

La cola debe ser segura para subprocesos. Usamos std::priority_queue (para que las tareas con menor tiempo de ejecución se extraigan primero) y std::mutex para la sincronización:

#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(); }

// Añadir una tarea con retraso 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

Recommendations