多线程05:unique_lock详解
📕unique_lock详解
一、unique_lock取代lock_guard#
- unique_lock是个类模板,实际应用中,一般lock_guard(推荐使用);lock_guard取代了mutex和的lock()和nulock(), 而unique_lock也取代mutex和的lock()和nulock();
- unique_lock比lock_guard灵活很多(多出来很多用法),效率差一点,内存占用多一点;
- 使用:
unique_lock<mutex> myUniLock(myMutex)
二、unique_lock的第二个参数#
2.1 std:: adopt_lock#
-
lock_guard中也可以用这个参数;
-
表示这个互斥量已经被lock(),即不需要在构造函数中lock这个互斥量了,前提:必须提前lock;
-
adopt_lock就是起一种标记作用,标志效果:“假设调用方线程已经拥有了互斥的所有权(即已经lock()成功了的);
2.2 std::try_to_lock#
- 意思:我们会尝试用mutex的lock()去锁定这个mutex,但是不同的是,如果没有锁定成功,也会立即返回,不会阻塞继续尝试锁定;
- 搭配owns_lock()方法判断是否拿到锁,如拿到返回true,没有拿到返回false;
- 前提:当下线程再使用该参数前,不要使用lock,不然没有意义,因为肯定拿不到;
- 使用try_to_lock的原因:是防止其他的线程锁定mutex太长时间,导致本线程一直阻塞在lock这个地方。
例子:
//写入数据函数;
void inMsgPro() {
for (int i = 0; i < 100; ++i) {
cout << "inMsgPro()执行,插入元素" << i << endl;
unique_lock<mutex> myUniLock(mutex1, try_to_lock);
if (myUniLock.owns_lock() == true) {
msgRecvQueue.push(i);
}
else {
cout << "拿不到锁" << endl;
//其他操作代码
}
}
}
2.3 std::defer_lock#
- 意思:加上defer_lock是初始化了一个没有加锁的mutex
- 前提:当下线程再使用该参数前,不要使用lock,不然没有意义,逻辑相悖;
- 使用std::defer_lock的原因:是以后可以调用unique_lock的一些方法
三、unique_lock的成员函数(前三个要与defer_lock联合使用)#
3.1 lock(): 加锁#
unique_lock<mutex> myUniLock(myMutex, defer_lock);
//加锁!
myUniLock.lock();
//注意:可以不用自己解锁,myUniLock对象析构的时候会进行unlock()操作;
3.2 unlock(): 解锁#
unique_lock<mutex> myUniLock(myMutex, defer_lock);
myUniLock.lock();//加锁!
//处理共享数据的代码
myUniLock.unlock();//解锁!
//处理非共享数据的代码(可能很多)
//.......
myUniLock.lock();//加锁!
//处理共享数据的代码
myUniLock.unlock();//解锁!
为什么有时候需要unlock():因为lock锁住的代码越多,锁的粒度越粗,执行效率就低;反而如果我们只锁住共享的代码,锁住代码少,锁的粒度细,执行效率高!
要选这合适的粒度:不能漏掉共享数据的保护,但是也不可以将其他不必要的代码加入!
3.3 try_lock(): 尝试给互斥量加锁#
- 如果拿到锁,则返回true,如果拿不到锁,函数不阻塞,返回false,继续往下执行;
这个操作和try_to_lock操作很像,个人感觉像是在defer_lock情况下加上这种不阻塞的功能;
3.4 release():#
unique_lock<mutex>myUniLock(myMutex);
相当于把myMutex和myUniLock绑定在了一起,而release()就是解除绑定,返回它所管理的mutex对象的指针,并释放所有权,不再指向mutex对象;mutex* ptx = myUniLock.release();
所有权由ptx接管,如果原来mutex对象进行了加锁,处于加锁状态,就需要ptx在后面进行解锁了;- 注意release()和unlock()的区别,一个是释放了所有权,一个只是释放锁,该对象还是和mutex绑定着;
四、unique_lock所有权的传递#
unique_lock<mutex> myUniLock(myMutex);
把myMutex和myUniLock绑定在了一起,也就是myUniLock拥有myMutex的所有权
所有权转移方式:
①使用move转移:
所有权可以转移,但是不能复制!
unique_lock<mutex> myUniLock(myMutex);
//unique_lock<mutex> myUniLock2(myUniLock); //复制所有权是非法,一种拷贝构造;
unique_lock<mutex> myUniLock2(std::move(myUniLock));//移动语言,传右值,调用带右值引用的拷贝构造,将myUniLock2和myMutex绑定一起,而myUniLock指向空!
②在函数中return一个临时变量,即可以实现转移
unique_lock<mutex> rtn_unique_lock()
{
unique_lock<mutex> myUniLock(myMutex);//位置1
//移动构造函数那里讲从函数返回一个局部的unique_lock对象是可以的
//返回这种局部对象会导致系统生成临时的unique_lock对象,并调用unique_lock的移动构造函数
return myUniLock;
}
// 然后就可以在外层调用,在myUniLock2具有对myMutex的所有权
std::unique_lock<std::mutex> myUniLock2 = rtn_unique_lock();//位置2
其实这种方法的本质是:用在函数中创建临时变量(位置1),将局部临时变量拷贝一份给调用函数(位置2,这里又有一份临时变量),最后再由位置2的临时变量 赋值给myUniLock2!
可以看出,是非常消耗内存,浪费资源时间的,因为位置1、2的临时对象构造马上又析构了,后面也不会用它们。所以强烈推荐使用move转移语义调用移动构造函数!
分类:
多线程 / c++多线程
, 多线程
标签:
多线程
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix