在使用互斥锁时要相当小心,如果某个线程忘记调用unlock()就返回,那么其他线程将永远拿不到线程锁。C++实现了对互斥锁的管理,管理所用到类模板和常量都定义在#include <mutex>
,利用作用域以及析构函数完成资源的自动管理,避免出现死锁的情况。
有一个类是用于控制管理工具加锁行为的:
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; };
结构体 | 行为 |
---|---|
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 |
下面介绍几种简单的管理工具,管理工具需要定义其调用的是lock
try_lock
还是根本就不进行lock
,分别对应defer_lock_t
try_to_clock_t
adopt_lock_t
,也就是后面提到的构造行为。
一、类模板 lock_guard
lock_guard是一个互斥量包装程序,它提供了一种方便的RAII(Resource acquisition is initialization )风格的机制来在作用域块的持续时间内拥有一个互斥量。创建lock_guard对象时,它将尝试获取提供给它的互斥锁的所有权。当控制流离开lock_guard对象的作用域时,lock_guard析构并释放互斥量。
template< class Mutex >
class lock_guard;//(since C++11)
lock_guard禁止了拷贝、赋值构造。构造函数有两种重载形式,如果线程尚未获得对互斥量所有权,那么应该调用构造函数explicit lock_guard( mutex_type& m );
lock_gaurd<std::mutex> lg_lo(lo);
否则结果是未定义的;如果线程已经获得互斥量所有权,那么应该调用第二种构造lock_guard( mutex_type& m, std::adopt_lock_t t );
lock_gaurd<std::mutex> lg_lo(lo,std::adopt_lock);//std::adopt_lock将会
若线程尚未获得所有权但是使用了第二个构造函数,那么结果是未定义的。此对象析构,将会调用unlock()。
类模板 unique_lock
unique_lock是一个通用的互斥量锁定包装器,它允许延迟锁定,限时深度锁定,递归锁定,锁定所有权的转移以及与条件变量一起使用。简单地讲,unique_lock 是 lock_guard 的升级加强版,它具有 lock_guard 的所有功能,同时又具有其他很多方法,使用起来更强灵活方便,能够应对更复杂的锁定需要。
template< class Mutex >
class unique_lock;
unique_lock可以std::move但是不可以拷贝。
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.
lock_guard和unique_lock的特点
lock_guard特点如下:
- 创建即加锁,作用域结束自动析构并解锁,无需手工解锁
- 不能中途解锁,必须等作用域结束才解锁
- 不能复制
unique_lock特点如下:
- 创建时可以不锁定(通过指定第二个参数为std::defer_lock),而在需要时再锁定
- 可以随时加锁解锁
- 作用域规则同 lock_grard,析构时自动释放锁
- 不可复制,可移动
- 条件变量需要该类型的锁作为参数(此时必须使用unique_lock)
[1] https://blog.csdn.net/guotianqing/article/details/104002449