C++11 thread library (5) mutual exclusion lock management tool

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_lockor 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.

  1. Constructs a unique_lock with no associated mutex.
  2. 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:
  3. 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.
  4. Does not lock the associated mutex.
  5. 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.
  6. Assumes the calling thread already owns m.
  7. 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.
  8. 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

Guess you like

Origin blog.csdn.net/weixin_39258979/article/details/114218957