En C++ moderno (desde C++11) existe una rica herramienta para trabajar con la multihilo: std::thread, mutexes y diversos objetos envolventes para gestionar el acceso a recursos. La interacción segura entre hilos requiere entender los siguientes componentes:
Para evitar condiciones de carrera, siempre usa envoltorios RAII donde sea posible. Ten en cuenta que intentar capturar el mismo mutex nuevamente llevará a un deadlock (si no es un mutex recursivo).
Ejemplo de trabajo correcto:
#include <iostream> #include <mutex> #include <thread> std::mutex mtx; void printThreadSafe(int id) { std::lock_guard<std::mutex> lock(mtx); std::cout << "Thread " << id << std::endl; } int main() { std::thread t1(printThreadSafe, 1); std::thread t2(printThreadSafe, 2); t1.join(); t2.join(); }
¿Se puede utilizar std::lock_guardstd::mutex para implementar sincronización condicional con std::condition_variable?
Respuesta: ¡No! Para trabajar con std::condition_variable es necesario un std::unique_lockstd::mutex, porque lock_guard no proporciona métodos para controlar el bloqueo (por ejemplo, liberar temporalmente — unlock — y luego volver a capturar), que son necesarios al esperar una notificación.
Ejemplo:
std::mutex mtx; std::condition_variable cv; bool ready = false; void waitForEvent() { std::unique_lock<std::mutex> lock(mtx); cv.wait(lock, []{return ready;}); // Procesamiento posterior }
Historia
En el proyecto se utilizaba trabajo manual con mtx.lock()/mtx.unlock(). Al lanzar una excepción entre lock/unlock, el mutex quedaba bloqueado — muchos hilos "se congelaban". Después de cambiar a RAII (
std::lock_guard), el problema desapareció.
Historia
Al notificar a través de std::condition_variable, en lugar de std::unique_lock, se utilizaba std::lock_guard. Como resultado, el programa no se compilaba en nuevos compiladores, y en el antiguo, ocurría un runtime assert debido a una API inconsistente.
Historia
En el código se llamaba unlock() a std::lock_guard para "controlar" manualmente el acceso al mutex. Esto llevaba a un comportamiento indefinido en diferentes compiladores, a veces causando la caída de la aplicación. Se entendió que lock_guard no admite liberación manual, era necesaria la migración a unique_lock.