0
0
CppDebug / FixIntermediate · 4 min read

How to Avoid Deadlock in C++: Causes and Fixes

Deadlock in C++ happens when two or more threads wait forever for locks held by each other. To avoid deadlock, always lock multiple 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.

cpp
#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;
}
Output
(Program hangs, no output due to deadlock)
🔧

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.

cpp
#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;
}
Output
Thread 1 acquired both locks Thread 2 acquired both locks
🛡️

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.

Key Takeaways

Always lock multiple mutexes in the same order to prevent deadlock.
Use std::lock to lock multiple mutexes simultaneously without deadlock.
Keep critical sections short and avoid nested locks when possible.
Use thread sanitizers and testing tools to detect deadlocks early.
Understand that deadlock happens when threads wait forever for each other's locks.