c:c_threads:troubleshooting:deadlocks
Table of Contents
C - C++ Threads - Troubleshooting - Deadlocks
The most common cause of a DEADLOCK is not acquiring multiple locks in the same order.
A situation where threads block indefinitely because they are waiting to acquire access to resources currently locked by other blocked threads.
Let's see an example:
Thread 1 | Thread 2 |
---|---|
Lock A | Lock B |
.. Do some processing | ..do some processing |
Lock B | Lock A |
.. Do some more processing | ..Do some more processing |
Unlock B | Unlock A |
Unlock A | Unlock B |
NOTE: In some situations, what is going to happen is that when Thread 1 tries to acquire Lock B, it gets blocked because Thread 2 is already holding lock B.
And from Thread 2's perspective, it is blocked on acquiring lock A, but cannot do so because Thread 1 is holding lock A.
Thread 1 cannot release lock A unless it has acquired lock B and so on.
In other words, your program is hanged at this point.
Example to simulate a deadlock
#include <iostream> #include <string> #include <thread> #include <mutex> using namespace std; std::mutex muA; std::mutex muB; void CallHome_AB(string message) { muA.lock(); //Some additional processing std::this_thread::sleep_for(std::chrono::milliseconds(100)); muB.lock(); cout << "Thread " << this_thread::get_id() << " says " << message << endl; muB.unlock(); muA.unlock(); } void CallHome_BA(string message) { muB.lock(); //Some additional processing std::this_thread::sleep_for(std::chrono::milliseconds(100)); muA.lock(); cout << "Thread " << this_thread::get_id() << " says " << message << endl; muA.unlock(); muB.unlock(); } int main() { thread t1(CallHome_AB, "Hello from Jupiter"); thread t2(CallHome_BA, "Hello from Pluto"); t1.join(); t2.join(); return 0; }
NOTE: If you run this, it will hang.
- Go ahead and break into debugger to look at the threads window and you will see that Thread 1 (calling function CallHome_Th1()) is trying to acquire mutex B while thread 2 (calling function CallHome_Th2()) is trying to acquire mutex A.
- None of them is making any progress because of the deadlock!
So, what can you do about it?
- The best thing to do is to structure your code in such a way that all locks are acquired in the same order.
- Depending on your situation,you can also employ the following strategies:
- Acquire locks together if both need to be acquired :
std::scoped_lock lock{muA, muB};
- You can use a timed mutex where you can mandate that a lock be released after a timeout if it is not already available.
References
c/c_threads/troubleshooting/deadlocks.txt · Last modified: 2021/06/07 08:56 by peter