En C++ moderne (à partir de C++11), il existe un riche ensemble d'outils pour travailler avec la multithreading : std::thread, mutex et divers objets d'encapsulation pour gérer l'accès aux ressources. Une interaction sécurisée entre les threads nécessite la compréhension des composants suivants :
Pour éviter les courses, utilisez toujours des encapsulations RAII là où c'est possible. Notez que tenter de capturer à nouveau le même mutex entraînera un deadlock (à moins qu'il ne s'agisse d'un mutex récursif).
Exemple de travail correct :
#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(); }
Peut-on utiliser std::lock_guardstd::mutex pour implémenter la synchronisation conditionnelle avec std::condition_variable ?
Réponse : Non ! Pour travailler avec std::condition_variable, il est absolument nécessaire d'utiliser std::unique_lockstd::mutex, car lock_guard ne fournit pas de méthodes pour contrôler le verrouillage (par exemple, relâcher temporairement — unlock — puis le reprendre), qui sont nécessaires lors de l'attente d'une notification.
Exemple :
std::mutex mtx; std::condition_variable cv; bool ready = false; void waitForEvent() { std::unique_lock<std::mutex> lock(mtx); cv.wait(lock, []{return ready;}); // Traitement ultérieur }
Histoire
Le projet utilisait un travail manuel avec mtx.lock()/mtx.unlock(). Lorsqu'une exception était levée entre lock/unlock, le mutex restait verrouillé — plusieurs threads "accrochaient". Après avoir remplacé par RAII (
std::lock_guard), le problème a disparu.
Histoire
Lors de la notification via std::condition_variable, std::lock_guard était utilisé au lieu de std::unique_lock. En conséquence, le programme ne se compilait pas sur de nouveaux compilateurs, tandis que sur l'ancien — une assertion d'exécution se produisait en raison d'une API incohérente.
Histoire
Dans le code, unlock() était appelé sur std::lock_guard pour un contrôle "manuel" de l'accès au mutex. Cela a conduit à un comportement indéfini sur différents compilateurs, parfois à un plantage de l'application. Il a été compris que lock_guard ne prend pas en charge le relâchement manuel, un passage à unique_lock était nécessaire.