Colas de tareas para C++: de la teoría a la práctica
La cola de tareas (task queue) es uno de los patrones clave de la programación multihilo en C++. Permite distribuir eficientemente el trabajo entre hilos, evitando condiciones de carrera y consumo excesivo de recursos. En este artículo, analizaremos cómo crear y usar colas de tareas en C++ con énfasis en el rendimiento y la seguridad.
¿Para qué sirve una cola de tareas?
En las aplicaciones modernas, a menudo surge la necesidad de ejecutar múltiples tareas pequeñas en paralelo. La creación directa de hilos para cada tarea genera sobrecarga: crear un hilo consume tiempo y memoria. La cola de tareas resuelve este problema mediante un pool de hilos (thread pool):
- Reutilización de hilos — los hilos no se destruyen tras ejecutar una tarea, sino que vuelven al pool.
- Balanceo de carga — las tareas se distribuyen de manera uniforme.
- Gestión de recursos — se limita la cantidad de hilos ejecutándose simultáneamente.
Implementación básica de una cola de tareas en C++
Veamos una cola de tareas simple pero segura para multihilo usando std::queue y mutex. Para ello, utilizamos la biblioteca estándar de C++11 y posteriores.
#include <queue>#include <mutex>#include <condition_variable>#include <functional>#include <thread>#include <vector>
class TaskQueue {public: using Task = std::function<void()>;
explicit TaskQueue(size_t numThreads) : stop(false) { for (size_t i = 0; i < numThreads; ++i) { workers.emplace_back([this] { workerLoop(); }); } }
~TaskQueue() { { std::unique_lock<std::mutex> lock(mutex); stop = true; } condition.notify_all(); for (auto& worker : workers) { if (worker.joinable()) { worker.join(); } } }
void enqueue(Task task) { { std::unique_lock<std::mutex> lock(mutex); tasks.push(std::move(task)); } condition.notify_one(); }
private: void workerLoop() { while (true) { Task task; { std::unique_lock<std::mutex> lock(mutex); condition.wait(lock, [this] { return stop || !tasks.empty(); }); if (stop && tasks.empty()) { return; } task = std::move(tasks.front()); tasks.pop(); } task(); // Ejecución de la tarea } }
std::queue<Task> tasks; std::mutex mutex; std::condition_variable condition; bool stop; std::vector<std::thread> workers;};Ejemplo de uso
Ahora mostramos cómo usar nuestra clase TaskQueue para ejecutar cálculos simples en paralelo:
#include <iostream>
int main() { TaskQueue tq(4); // Pool de 4 hilos
for (int i = 0; i < 10; ++i) { tq.enqueue([i] { std::cout << "Tarea " << i << " ejecutada por el hilo " << std::this_thread::get_id() << std::endl; }); }
// El destructor de tq esperará a que todas las tareas terminen return 0;}Funcionalidades avanzadas: cola con prioridades
A veces es necesario procesar las tareas según su importancia. Para ello, se puede usar std::priority_queue en lugar de una cola normal. Añadimos soporte para prioridades:
#include <queue>#include <functional>
class PriorityTaskQueue {public: using Task = std::function<void()>;
struct PrioritizedTask { int priority; Task t También en la biblioteca
node-cron: Programador de tareas para JavaScript (Node.js)
Trabajo con S3 y almacenamiento en la nube en C# (.NET): AWS SDK y MinIO
Trabajo con archivos ZIP en Rust: biblioteca zip y guía completa
Redis (ioredis) para JavaScript (Node.js): guía completa con ejemplos