Concurrencia y Multihilo en C++: Nivel Avanzado

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

Concurrencia y Multihilo en C++: De los Fundamentos a las Técnicas Avanzadas



Las aplicaciones modernas exigen cada vez más un rendimiento máximo, especialmente al trabajar con procesadores multinúcleo. C++ ofrece potentes herramientas para trabajar con concurrencia y multihilo, desde C++11 hasta los estándares más recientes. En este artículo, analizaremos aspectos avanzados del multihilo: desde el uso correcto de mutexes y operaciones atómicas hasta patrones complejos de sincronización. El material está dirigido a desarrolladores que ya conocen los fundamentos de los hilos en C++.



1. Gestión de Hilos: std::thread y std::jthread



La clase std::thread es la base de la programación multihilo en C++. Sin embargo, a nivel avanzado, es importante no solo crear un hilo, sino también gestionar correctamente su ciclo de vida. Con C++20 apareció std::jthread, que se une automáticamente al destruirse, evitando fugas de recursos.



Ejemplo de uso de std::jthread:



#include <iostream>#include <thread>#include <chrono>

void worker(int id) { std::this_thread::sleep_for(std::chrono::milliseconds(100 * id)); std::cout << "Hilo " << id << " finalizado";}

int main() { // std::jthread se une automáticamente al salir del ámbito std::jthread t1(worker, 1); std::jthread t2(worker, 2); std::jthread t3(worker, 3); // No es necesario llamar explícitamente a join() o detach() // Al destruirse t1, t2, t3, main espera su finalización std::cout << "El hilo principal continúa trabajando"; return 0;}


Es importante recordar: std::jthread también admite el mecanismo de detención mediante std::stop_token, lo que permite finalizar de forma segura tareas largas en segundo plano.



2. Sincronización de Acceso: Mutexes, Bloqueos y Variables de Condición



El principal problema del multihilo son las condiciones de carrera. Para prevenirlas se utilizan mutexes. A nivel avanzado, se recomienda usar std::scoped_lock (C++17) para capturar varios mutexes sin riesgo de interbloqueo.



Ejemplo con std::scoped_lock:



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

std::mutex mtx1, mtx2;int shared_data = 0;

void safe_increment(int id) { // scoped_lock captura ambos mutexes en orden, evitando deadlock std::scoped_lock lock(mtx1, mtx2); ++shared_data; std::cout << "Hilo " << id << " incrementó el valor a " << shared_data << "";}

int main() { std::vector<std::jthread> threads; for (int i = 0; i < 10; ++i) { threads.emplace_back(safe_increment, i); } return 0;}


Para escenarios de sincronización más complejos (por ejemplo, productor-consumidor) se utilizan variables de condición (std::condition_variable). Permiten que los hilos esperen la ocurrencia de un evento específico sin desperdiciar recursos en espera activa.



3. Operaciones Atómicas y el Modelo de Memoria de C++



Las operaciones atómicas (std::atomic) son la base de la programación sin bloqueos (lock-free). Garantizan que la lectura y escritura de una variable se realicen como una única acción indivisible. Sin embargo, a nivel avanzado es importante comprender el modelo de memoria de C++ y los niveles de ordenamiento (memory ordering).



Niveles principales de memory ordering:

  • memory_order_relaxed — garantías mínimas, solo atomicidad
  • memory_order_acquire — garantiza que todas las lecturas y escrituras posteriores vean los resultados de operaciones anteriores
  • memory_order_release — garantiza que todas las operaciones anteriores sean visibles después de esta escritura
  • memory_order_acq_rel — combinación de acquire y release

Blogs

Book Recommendations