一、unique_lock取代lock_guard
unique_lock是个类模板,工作中,一般lock_guard(推荐使用);
lock_guard取代了mutex的lock()和unlock()。
unique_lock比lock_guard灵活很多灵活很多;效率上差一点,内存占用多一点。
std::lock_guard<std::mutex> sbguard(my_mutex);
直接换成std::unique_lock<std::mutex> sbguard(my_mutex);
二、unique_lock的第二个参数
lock_guard可以带第二个参数:
std::lock_guard<std::mutex> sbguard(my_mutex,std::adopt_lock);
//adopt_lock标记作用,unique_lock也支持
2.1 std::adopt_lock:
表示这个互斥量已经被lock了(你必须提前把互斥量lock了)
std::adopt_lock标记的效果就是假设调用方线程已经拥有了互斥的所有权(已经lock()成功了),通知lock_guard不需要在构造函数中lock这个互斥量了
unique_lock也可以带std::adopt_lock标记,含义相同
2.2 std::try_to_lock
【引言】
std::mutex::lock特点:
拿到锁之后立即返回,拿不到锁一直卡着。
我们会尝试用mutex的lock()去锁定这个mutex,但如果没锁定成功,我也会立即返回,并不会阻塞在那里;
用这个try_to_lock的前提是你自己不能先去lock。
std::unique_lock<std::mutex> sb_guard(my_mutex1,std::try_to_lock);
if (sb_guard.owns_lock())
{
msgRecvQueue.push_back(i);
}
else
{
cout << "inMsgRecvQueue()执行,但没有拿到锁,只能干点别的事" << i << endl;
}
2.1 2.2小结:
adopt_lock前提要把互斥量锁住了,try_to_lock的前提是你自己不能先去lock(只能用unique_lock调用构造函数锁)
2.3 std::defer_lock
前提是你不能自己先lock,否则会报异常。
意思是就是没有给mutex加锁,初始化了一个没有加锁的mutex。
我们借着unique_lock的话题,来介绍一些unqiue_lock的重要成员函数
std::unique_lock<std::mutex> sbguard(my_mutex1,std::defer_lock);//没有加锁的my_mutex
sbguard.lock(); //咱们不用自己unlock()
三、unique_lock的成员函数
3.1 lock()
3.2 unlock()
std::unique_lock<std::mutex> sbguard(my_mutex1,std::defer_lock);//没有加锁的my_mutex
sbguard.lock(); //咱们不用自己unlock()
//因为有一些非共享的代码要处理
sbguard.unlock();
//处理一些非共享代码
sbguard.lock();
msgRecvQueue.push_back(i);
sbguard.unlock(); //画蛇添足,但也可以
3.3 try_lock()
尝试给互斥量加锁,如果拿不到锁,则返回false,如果拿到了锁,返回true,这个函数不阻塞
std::unique_lock<std::mutex> sbguard(my_mutex1,std::defer_lock);//没有加锁的my_mutex
if (sbguard.try_lock() == true)
{
msgRecvQueue.push_back(i);
}
else
{
cout << "inMsgRecvQueue()执行,但没有拿到锁,只能干点别的事" << i << endl;
}
std::unique_lock::try_lock()
和std::defer_lock()
配合使用
3.4 release()
返回它所管理的mutex对象指针,并释放所有权;也就是说,这个unique_lock和mutex不再有关系
严格区分unlock()和release()区别,不要混淆。
std::unique_lock<std::mutex> sbguard(my_mutex1);
相当于把unique_lock和std::mutex类对象my_mutex1绑定在一起
如果原来mutex对象处于加锁状态,你有责任接管过来并负责解锁。
std::unique_lock<std::mutex> sbguard(my_mutex1);//没有加锁的my_mutex
std::mutex* ptx = sbguard.release();
msgRecvQueue.push_back(i);
ptx->unlock();
注意:
lock锁住的代码段越少,执行越快,整个程序运行效率越高。
lock锁住的代码多少,称为锁的粒度,粒度一般用粗细来描述;
a) 锁住的代码少,这个粒度叫细。执行效率高;
b) 锁住的代码多,粒度叫粗,执行效率就低。
四、unique_lock所有权的传递
unique_lock需要和一个mutex对象绑定到一起发挥作用(配合使用、管理)
不能将一个mutex对象和两个unique_lock绑定在一起
这就是所有权的概念
std::unique_lock<std::mutex> sbguard(my_mutex1);
sbguard拥有my_mutex1的所有权
sbguard可以把自己对mutex类对象(my_mutex1)的所有权转移给其他unique_lock对象;
所以,unique_lock对象 这个mutex所有权是属于可以转移,但是不能复制。
4.1 通过std::move()转移所有权
std::unique_lock<std::mutex> sbguard(my_mutex1);
std::unique_lock<std::mutex> sbguard2(std::move(sbguard));//移动语义,现在相当于sbguard2和my_mutex1绑定到一起了,现在sbguard指向空,sbguard已经解除了my_mutex1的管理,由sbguard2接管
4.2 通过函数返回值return std::unique_lock<std::mutex>转移所有权
std::unique_lock<std::mutex> rtn_unique_lock()
{
std::unique_lock<std::mutex> tmpguard(my_mutex1);
return tmpguard; //从函数返回一个局部的unique_lock对象是可以的。三章14节讲过移动构造函数。
//返回这种局部对象tmpguard会导致系统生成临时的unique_lock对象,并调用unqiue
}