Concurrencia y multihilo en C++: de la A a la Z

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

Introducción a la concurrencia y el multihilo en C++



Los procesadores modernos tienen múltiples núcleos, y para aprovechar toda su potencia, los programas deben ser capaces de ejecutar varias tareas simultáneamente. La concurrencia (concurrency) es la capacidad de un programa para ejecutar varias tareas en intervalos de tiempo superpuestos, y el multihilo (multithreading) es una de las formas de implementarla, donde el programa se divide en varios hilos que trabajan en paralelo.



En C++, el soporte para multihilo apareció en el estándar C++11 y desde entonces se ha desarrollado activamente. En este artículo, analizaremos todos los aspectos clave: desde la creación de hilos hasta patrones complejos de sincronización.



Fundamentos: creación y gestión de hilos



std::thread — el bloque de construcción básico



La clase std::thread permite ejecutar una función o un objeto invocable en un hilo separado. Veamos un ejemplo sencillo:



#include <iostream>#include <thread>

void hello() { std::cout << "Saludo desde el hilo " << std::this_thread::get_id() << std::endl;}

int main() { std::thread t(hello); // Iniciamos el hilo t.join(); // Esperamos a que termine el hilo return 0;}


El método join() obliga al hilo principal a esperar a que termine el hilo secundario. La alternativa es detach(), que permite que el hilo funcione de forma independiente, pero requiere especial cuidado (el hilo puede sobrevivir a los objetos que lo crearon).



Pasar argumentos y funciones lambda



Se pueden pasar argumentos a los hilos y usar lambdas:



#include <thread>#include <string>

void process(int id, const std::string& data) { // Procesamiento de datos}

int main() { std::thread t1(process, 1, "datos"); std::thread t2([](int x) { // Expresión lambda }, 42); t1.join(); t2.join();}


Sincronización de hilos: protección de datos compartidos



Cuando varios hilos acceden a los mismos datos, se produce una condición de carrera (race condition). Para evitarlo, se utilizan mecanismos de sincronización.



Mutex (std::mutex)



Un mutex es un primitivo de sincronización que garantiza que solo un hilo puede poseerlo en un momento dado:



#include <iostream>#include <thread>#include <mutex>

std::mutex mtx;int counter = 0;

void increment() { for (int i = 0; i < 100000; ++i) { std::lock_guard<std::mutex> lock(mtx); ++counter; // Acceso protegido }}

int main() { std::thread t1(increment); std::thread t2(increment); t1.join(); t2.join(); std::cout << "Counter: " << counter << std::endl; return 0;}


std::lock_guard es un envoltorio RAII que captura automáticamente el mutex al crearse y lo libera al destruirse. Esto protege contra el olvido accidental de liberar el mutex.



Variables de condición (std::condition_variable)



Las variables de condición permiten que los hilos esperen a que ocurra un evento determinado:



#include <queue>#include <condition_variable>

std::queue<int> data_queue;std::mutex queue_mutex;std::condition_variable cv;

void producer() { for (int i = 0; i < 10; ++i) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::lock_guard<std::mutex> lock(queue_mutex); data_queue.push(i); cv.notify_one(); // Notificamos al consumidor }}

void consumer() { while (true) { std::unique_lock<std::mutex> lock(queue_mutex); cv.wait(lock, []{ return !data_queue.empty(); }); int value = data_queue.front(); data_queue.pop(); lock.unlock(); // Procesa

Blogs

Book Recommendations