How to Avoid Deadlock in C++: Causes and Fixes
std::mutex objects in the same order or use std::lock to lock them simultaneously without waiting.Why This Happens
Deadlock occurs when two threads each hold a lock the other needs, causing both to wait forever. This happens if locks are acquired in different orders, creating a cycle of waiting.
#include <iostream> #include <thread> #include <mutex> std::mutex mutexA; std::mutex mutexB; void thread1() { std::lock_guard<std::mutex> lockA(mutexA); std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::lock_guard<std::mutex> lockB(mutexB); std::cout << "Thread 1 acquired both locks\n"; } void thread2() { std::lock_guard<std::mutex> lockB(mutexB); std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::lock_guard<std::mutex> lockA(mutexA); std::cout << "Thread 2 acquired both locks\n"; } int main() { std::thread t1(thread1); std::thread t2(thread2); t1.join(); t2.join(); return 0; }
The Fix
To fix deadlock, lock both mutexes at once using std::lock which locks multiple mutexes without waiting in a deadlock cycle. Then use std::lock_guard with std::adopt_lock to manage the locks safely.
#include <iostream> #include <thread> #include <mutex> std::mutex mutexA; std::mutex mutexB; void thread1() { std::lock(mutexA, mutexB); std::lock_guard<std::mutex> lockA(mutexA, std::adopt_lock); std::lock_guard<std::mutex> lockB(mutexB, std::adopt_lock); std::cout << "Thread 1 acquired both locks\n"; } void thread2() { std::lock(mutexA, mutexB); std::lock_guard<std::mutex> lockA(mutexA, std::adopt_lock); std::lock_guard<std::mutex> lockB(mutexB, std::adopt_lock); std::cout << "Thread 2 acquired both locks\n"; } int main() { std::thread t1(thread1); std::thread t2(thread2); t1.join(); t2.join(); return 0; }
Prevention
To avoid deadlocks in the future, always lock multiple mutexes in the same order across all threads. Use std::lock for locking multiple mutexes together safely. Keep critical sections short and avoid holding locks while calling other functions that might lock again. Tools like thread sanitizers can help detect deadlocks during testing.
Related Errors
Similar issues include livelocks where threads keep retrying without progress, and race conditions where shared data is accessed without proper locking. Using std::mutex correctly and following locking order rules helps prevent these problems.