Assuming there are two threads, you need to lock a pair of mutexes when performing certain operations, thread a locks up mutex a, and thread B locks up the amount of mutex B, which is waiting for the other person to release another mutex, which can cause the two threads to continue executing. This situation is a deadlock.
The simplest way to avoid deadlocks is to always lock two mutexes in the same order, such as always lock mutex A before locking mutex B, and never deadlock.
Suppose there is an operation to exchange the contents of two instances of the same class, in order for the interchange operation not to be affected by concurrent modification, we need to lock the mutex inside these two instances. However, if a fixed order is selected to lock the mutex (the line locks the mutex of the object specified by the first parameter, and then locks the mutex of the second parameter to the specified object), it is counterproductive and causes a deadlock.
In this case, we need to use the std::lock operation of the C++11 standard library to solve this problem. The lock function can accept two or more mutexes to avoid deadlocks.
Examples are as follows:
#include <mutex>class some_big_object{};void swap (some_big_object& lhs,some_big_object& rhs) {}class X{ Private: some_big_object some_detail; mutable Std::mutex m;public: x (some_big_object const& SD): Some_detail (SD) {} friend void swap (x& LHS, X & RHS) { //To determine if two instances are the same, and if the same exits directly //Because the result of locking a mutex that has been locked by this thread is indeterminate if (&LHS==&RHS) return; Lock two mutex std::lock (LHS.M,RHS.M); Create two Lock_guard instances, passing them a mutex object //Std::adopt_lock parameter indicating that the incoming mutex has been locked, and that they only need to accept ownership of the locked mutex // Instead of trying to lock the incoming mutex in its constructor std::lock_guard<std::mutex> lock_a (lhs.m,std::adopt_lock); Std::lock_guard<std::mutex> Lock_b (rhs.m,std::adopt_lock); Swap (Lhs.some_detail,rhs.some_detail); }}; int main () {}
Use Std::lock_guard to ensure that locked mutexes are also correctly unlocked when the program throws an exception.
The Std::lock operation guarantees that the first mutex will be automatically unlocked if an exception occurs when attempting to lock the second mutex after the first mutex is successfully locked. The Std::lock operation can only help us avoid deadlocks when locking multiple mutexes, and if these mutexes are individually locked, we need to ensure that the program does not deadlock when implemented. Let's take a look at some basic ways to avoid deadlocks:
1. Avoid nested locks:
If each thread can lock only one mutex, no deadlock occurs. If you want to use multiple locks, use Std::lock.
2. Avoid invoking user-supplied code after a mutex is locked:
We cannot guarantee what the user code does, and it is likely to lock other mutexes and lead to deadlocks.
3. Lock the mutex in a fixed order:
If you do need to lock multiple mutexes, and these mutexes can be locked at the same time, use the previous Std::lock method to secure the order of locked mutexes and avoid deadlocks. If these mutexes can only be locked separately, it is necessary to ensure that the order of the locked mutexes is fixed when implemented, such as always lock a before locking B and lock B before locking C.
4. Use a layered lock:
Divide the program into several levels. The lock used in each hierarchy is distinguished, and a high-level lock is not allowed when a thread already holds a lower lock. You can add a hierarchy number to different locks while the program is running, and record the locks held by each thread.
#include <stdexcept> #include <thread> #include <mutex>class hierarchical_mutex//implements the three interface lock of the mutex, Unlock,trylock{std::mutex internal_mutex;unsigned Long const hierarchy_value;//record mutex hierarchy unsigned long previous_ hierarchy_value;//records the hierarchy of the previous mutex, restores the thread's hierarchy when unlocked//thread_local each thread has its own instance of that global variable (instance) static thread_local unsigned Long this_thread_hierarchy_value;//thread level, is Thread_localvoid check_for_hierarchy_violation () {// The thread is at the same level as the current mutex, otherwise throws an exception if (This_thread_hierarchy_value <= hierarchy_value) {throw std::logic_error ("mutex hierarchy violated ");}} void Update_hierarchy_value () {previous_hierarchy_value = This_thread_hierarchy_value;this_thread_hierarchy_value = Hierarchy_value;} Public:explicit Hierarchical_mutex (unsigned Long value): Hierarchical_mutex (value), Previous_hierarchy_value (0) {} void Lock () {//Check first, re-lock, update hierarchy check_for_hierarchy_violation (); Internal_mutex.lock (); Update_hierarchy_value ();} void Unlock () {//update hierarchy, re-unlock this_thread_hierarchy_value = Previous_hierarchy_value;inTernal_mutex.unlock ();} BOOL Try_lock () {check_for_hierarchy_violation (); if (!internal_mutex.try_lock ()) return False;update_hierarchy_ Value (); return true;}}; thread_local unsigned long hierarchical_mutex::this_thread_hierarchy_value (Ullong_max);
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
[c++11 concurrent programming] 06-mutex deadlock