1. std::lock_guard
c++11引入的类模板,用于取代lock和unlock;
template<typename _Mutex>
class lock_guard
{
public:
typedef _Mutex mutex_type;
//构造自动加锁
explicit lock_guard(mutex_type& __m) : _M_device(__m)
{ _M_device.lock(); }
lock_guard(mutex_type& __m, adopt_lock_t) noexcept : _M_device(__m)
{ } // calling thread owns mutex
//析构自动解锁
~lock_guard()
{ _M_device.unlock(); }
lock_guard(const lock_guard&) = delete;
lock_guard& operator=(const lock_guard&) = delete;
private:
mutex_type& _M_device;
};
原理: 构造自动加锁,析构自动解锁;离开作用域时调用析构自动解锁;
{
std::lock_guard<std::mutex> lock(mtx);
// 临界区代码
} // 自动解锁
2. std::unique_lock
类模板,功能与lock_guard类似,但比lock_guard灵活;unique_lock可以完全取代lock_guard,
std::unique_lock<std::mutex> t_lock(mutex);
unique_lock还存在第二参数,可灵活配置;
2.1 adopt_lock
lock_guard和unique_lock都可以带第二个参数std::adopt_lock;
它表示这个互斥量已经被锁定了,锁定对象在构造时不会尝试锁定互斥量,而是"收养"已存在的锁;
这种策略通常用于管理已经锁定的互斥量;
场景1:
{
g_mutex.lock(); //加锁
//std::unique_lock<std::mutex> t_lock1(g_mutex, std::adopt_lock);
//不带 std::adopt_lock参数,则构造函数会加锁,而上面已经加锁,那么这里会导致死锁;
std::unique_lock<std::mutex> t_lock1(g_mutex);
}
场景2:
{
g_mutex.lock(); //加锁
//带 std::adopt_lock参数,构造函数不会加锁,一切正常
std::unique_lock<std::mutex> t_lock1(g_mutex, std::adopt_lock);
//std::unique_lock<std::mutex> t_lock1(g_mutex);
}
场景3:
{
//g_mutex.lock();
//带 std::adopt_lock参数,构造函数不会加锁; 而前面没有lock,后续的代码可能存在风险!!!
std::unique_lock<std::mutex> t_lock1(g_mutex, std::adopt_lock);
//std::unique_lock<std::mutex> t_lock1(g_mutex);
}
总结: 使用std::adopt_lock的前提是开发者需要先把互斥量lock上。
2.2 try_to_lock
系统尝试去lock这个mutex;没锁成功,也会立即返回,并不会阻塞;
场景1:
{
//g_mutex.lock();
std::unique_lock<std::mutex> t_lock1(g_mutex, std::try_to_lock);
if(t_lock1.owns_lock()){
//打印
std::cout << "Lock acquire ok" << std::endl;
}else{
std::cout << "timeout failed to acquire lock" << std::endl;
}
}
场景2:
{
g_mutex.lock();
std::unique_lock<std::mutex> t_lock1(g_mutex, std::try_to_lock);
if(t_lock1.owns_lock()){
std::cout << "Lock acquire ok" << std::endl;
}else{
//打印
std::cout << "failed to acquire lock" << std::endl;
}
}
2.3 defer_lock
初始化这个mutex, 但是不去加锁;通过这个没加锁的mutex,可灵活调用其成员函数;
2.4 unique_lock成员函数
2.4.1 lock()
给互斥量加锁,若无法加锁,将会阻塞等待;
2.4.2 try_lock()
尝试给互斥量解锁,加锁失败返回false; 不阻塞;
2.4.3 unlock()
解锁互斥量,若解锁未上锁的互斥量将会抛出异常;
terminate called after throwing an instance of 'std::system_error'
what(): Operation not permitted
2.4.4 release()
返回锁管理的mutex指针,并释放所有权;也就是unique_lock和mutex将没有关系;程序员需要对返回的mutex进行管理;
2.4.5 owns_lock()
判断是否已经锁成功,锁成功返回true;
2.4.6 所有权移动
unique_lock的mutex的所有权是可移动的,但不能复制;
std::unique_lock<std::mutex> t_lock1(g_mutex);
//std::unique_lock<std::mutex> t_lock2(t_lock1); //不支持复制所有权
std::unique_lock<std::mutex> t_lock3(std::move(t_lock1));//所有权移动
3. lock_guard和unique_lock比较
特性 | std::lock_guard | std::unique_lock |
---|---|---|
手动解锁 | 支持lock()/unlock() | 不支持 |
延迟锁定 | std::defer_lock | 不支持 |
尝试锁定 | try_to_lock | 不支持 |
性能开销 | 略高(功能更多) | 更低(更轻量) |
所有权转移 | std::move | 不支持 |