Concurrencia y Multihilo en C++: Guía Completa

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++



Las aplicaciones modernas requieren el máximo rendimiento. La concurrencia y el multihilo en C++ permiten utilizar eficientemente procesadores multinúcleo, acelerando la ejecución de tareas. Sin comprender estos conceptos, es imposible crear sistemas de alta carga, procesadores de datos o motores de juegos.



En este artículo, analizaremos los mecanismos clave del multihilo en C++, comenzando con std::thread básico y terminando con patrones avanzados. Aprenderá cómo evitar condiciones de carrera, sincronizar hilos correctamente y escribir código paralelo seguro.



Fundamentos del multihilo: std::thread y std::jthread



La clase std::thread (C++11) es la base de la programación multihilo. Permite ejecutar una función o un objeto invocable en un hilo separado. Veamos un ejemplo simple:



#include <iostream>#include <thread>

void worker(int id) { std::cout << "Hilo " << id << " comenzó a trabajar"; std::this_thread::sleep_for(std::chrono::seconds(1)); std::cout << "Hilo " << id << " finalizado";}

int main() { std::thread t1(worker, 1); std::thread t2(worker, 2); t1.join(); t2.join(); return 0;}


Puntos clave:

  • join() — el hilo principal espera a que el hilo hijo termine
  • detach() — el hilo continúa trabajando de forma independiente (riesgo de fuga de recursos)
  • std::jthread (C++20) — llama automáticamente a join() en el destructor


Siempre verifique joinable() antes de llamar a join() para evitar una doble unión.



Sincronización de hilos: mutex y bloqueos



El principal problema del multihilo son las condiciones de carrera, cuando varios hilos modifican simultáneamente una variable compartida. Para protegerlas se utilizan mutex.



std::mutex y std::lock_guard



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

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


std::lock_guard libera automáticamente el mutex al salir del ámbito. Para un control más flexible, use std::unique_lock.



Deadlock y std::scoped_lock



Un bloqueo mutuo (deadlock) ocurre cuando dos hilos se esperan el uno al otro. La solución es usar std::scoped_lock (C++17), que captura múltiples mutex de forma atómica:



std::mutex m1, m2;

void safe_function() { std::scoped_lock lock(m1, m2); // trabajo seguro con dos recursos}


Algoritmos paralelos y std::async



Para ejecutar tareas asíncronas sin gestionar hilos manualmente, use std::async. Devuelve un std::future del cual se puede obtener el resultado.



#include <iostream>#include <future>#include <chrono>

int compute(int n) { std::this_thread::sleep_for(std::chrono::seconds(n)); return n * n;}

int main() { auto future1 = std::async(std::launch::async, compute, 2); auto future2 = std::async(std::launch::async, compute, 3); std::cout << "Resultados: " << future1.get() << ", " << future2.get() << ""; // 4, 9 return 0;}


Políticas de lanzamiento:

  • std::launch::async — lanzamiento en un nuevo hilo
  • std::launch::deferred — ejecución diferida (perezosa)

Blogs

Book Recommendations