Be careful when using mutex locks. If a thread forgets to call unlock() and returns, then other threads will never get the thread lock. C++ implements the management of mutual exclusion locks. The class templates and constants used for management are defined in #include <mutex>
. The scope and destructor are used to complete the automatic management of resources to avoid deadlocks.
There is a class used to control the locking behavior of management tools:
struct defer_lock_t {
explicit defer_lock_t() = default; };
struct try_to_lock_t {
explicit try_to_lock_t() = default; };
struct adopt_lock_t {
explicit adopt_lock_t() = default; };
Structure | behavior |
---|---|
defer_lock_t | do not acquire ownership of the mutex |
try_to_clock_t | try to acquire ownership of the mutex without blocking |
adopt_lock_t | assume the calling thread already has ownership of the mutex |
Here are a few simple management tools. The management tool needs to define whether it is called lock
try_lock
or not at all lock
, corresponding respectively defer_lock_t
try_to_clock_t
adopt_lock_t
, which is the construction behavior mentioned later.
1. Class template lock_guard
lock_guard is a mutex wrapper, which provides a convenient RAII (Resource acquisition is initialization) style mechanism to have a mutex for the duration of the scope block. When a lock_guard object is created, it will try to acquire ownership of the mutex provided to it. When the control flow leaves the scope of the lock_guard object, lock_guard deconstructs and releases the mutex.
template< class Mutex >
class lock_guard;//(since C++11)
lock_guard prohibits copy and assignment construction. The constructor has two overloaded forms, if the thread has not yet obtained ownership of the mutex, then the constructor should be calledexplicit lock_guard( mutex_type& m );
lock_gaurd<std::mutex> lg_lo(lo);
Otherwise, the result is undefined; if the thread has acquired ownership of the mutex, the second construct should be calledlock_guard( mutex_type& m, std::adopt_lock_t t );
lock_gaurd<std::mutex> lg_lo(lo,std::adopt_lock);//std::adopt_lock将会
If the thread has not yet acquired ownership but uses the second constructor, the result is undefined. This object is destroyed and unlock() will be called.
Class template unique_lock
unique_lock is a universal mutex lock wrapper that allows delayed locking, time-limited deep locking, recursive locking, transfer of lock ownership, and use with condition variables. Simply put, unique_lock is an upgraded and enhanced version of lock_guard. It has all the functions of lock_guard, as well as many other methods. It is more flexible and convenient to use, and can cope with more complex locking needs.
template< class Mutex >
class unique_lock;
unique_lock can be std::move but cannot be copied.
unique_lock() noexcept;(1) (since C++11)
unique_lock( unique_lock&& other ) noexcept;(2) (since C++11)
explicit unique_lock( mutex_type& m );(3) (since C++11)
unique_lock( mutex_type& m, std::defer_lock_t t ) noexcept;(4) (since C++11)
unique_lock( mutex_type& m, std::try_to_lock_t t );(5) (since C++11)
unique_lock( mutex_type& m, std::adopt_lock_t t );(6) (since C++11)
template< class Rep, class Period >
unique_lock( mutex_type& m,
const std::chrono::duration<Rep,Period>& timeout_duration );(7) (since C++11)
template< class Clock, class Duration >
unique_lock( mutex_type& m,
const std::chrono::time_point<Clock,Duration>& timeout_time );(8) (since C++11)
Constructs a unique_lock, optionally locking the supplied mutex.
- Constructs a unique_lock with no associated mutex.
- Move constructor. Initializes the unique_lock with the contents of other. Leaves other with no associated mutex.
3-8) Constructs a unique_lock with m as the associated mutex. Additionally: - Locks the associated mutex by calling m.lock(). The behavior is undefined if the current thread already owns the mutex except when the mutex is recursive.
- Does not lock the associated mutex.
- Tries to lock the associated mutex without blocking by calling m.try_lock(). The behavior is undefined if the current thread already owns the mutex except when the mutex is recursive.
- Assumes the calling thread already owns m.
- Tries to lock the associated mutex by calling m.try_lock_for(timeout_duration). Blocks until specified timeout_duration has elapsed or the lock is acquired, whichever comes first. May block for longer than timeout_duration.
- Tries to lock the associated mutex by calling m.try_lock_until(timeout_time). Blocks until specified timeout_time has been reached or the lock is acquired, whichever comes first. May block for longer than until timeout_time has been reached.
The characteristics of lock_guard and unique_lock
The characteristics of lock_guard are as follows:
- It is locked when created, and the scope is automatically destroyed and unlocked at the end of the scope, without manual unlocking
- Can not be unlocked halfway, you must wait for the end of the scope to unlock
- Can't copy
The unique_lock features are as follows:
- It can be created without locking (by specifying the second parameter as std::defer_lock), and then locking when needed
- Can be locked and unlocked at any time
- The scope rules are the same as lock_grard, and the lock is automatically released during destructuring
- Can not be copied, can be moved
- The condition variable requires this type of lock as a parameter (unique_lock must be used at this time)
[1] https://blog.csdn.net/guotianqing/article/details/104002449