C++11并发与多线程笔记(6)unique_lock详解
2021/11/6 11:09:39
本文主要是介绍C++11并发与多线程笔记(6)unique_lock详解,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
文章目录
- 1、unique_lock取代lock_guard
- 2、unique_lock第二个参数
- 2.1 std::adopt_lock
- 2.2 std::try_to_lock
- 2.3 std::defer_lock
- 3、unique_lock的成员函数
- 3.1 lock(),unlock()
- 3.2 try_lock())
- 3.3 release()
- 4、unique_lock所有权的传递
- 4.1 std::move
- 4.2 return std::unique_lock
1、unique_lock取代lock_guard
unique_lock
- 是一个类模板,工作中,一般用
lock_guard
(推荐使用);lock_guard
取代了mutex
的lock()
和unlock()
; unique_lock
比lock_guard
灵活很多;效率上差一点,内存占用多一点。- 常规使用,参数只有一个互斥量时,没有什么区别。
2、unique_lock第二个参数
2.1 std::adopt_lock
std::adopt_lock
std::adopt_lock
:表示这个互斥量已经被lock
了(使用前必须要把互斥量提前lock了,否则会报异常)。std::adopt_lock
标记的效果就是“假设调用方线程已经拥有了互斥的所有权(就是已经lock()
成功了)。- 通知
unique_lock
不需要在构造函数中lock()
这个互斥量了。
示例代码:
#include <iostream> #include <thread> #include <vector> #include <list> #include <mutex> using namespace std; class A { public: //把收到的消息(玩家命令)入到一个队列的线程 void inMsgRecvQueue() { for (int i = 0; i < 10000; i++) { cout << "inMsgRecvQueue()执行,插入一个元素:" << i << endl; my_mutex.lock(); //要先lock,后续才能用unique_lock的std::adopt_lock参数 std::unique_lock<std::mutex> sbguard1(my_mutex,std::adopt_lock); msgRecvQueue.push_back(i); } return; } bool outMsgLULProc(int& command) { std::unique_lock<std::mutex> sbguard1(my_mutex); if (!msgRecvQueue.empty()) { //消息不为空 command = msgRecvQueue.front(); //返回第一个元素,但不检查元素是否存在 msgRecvQueue.pop_front(); //移除第一个元素,但不返回 return true; } return false; } //把数据从消息队列中取出的线程 void outMsgRecvQueue() { int command = 0; for (int i = 0; i < 10000; i++) { bool result = outMsgLULProc(command); if (result == true) { cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl; //接下来就考虑处理数据...... } else { //消息队列为空 cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl; } } } private: list<int> msgRecvQueue; //容器,专门用于代表玩家给咱们发送过来的命令 mutex my_mutex; //创建了一个互斥量 }; int main() { A myobja; thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja); thread myInMsgObj(&A::inMsgRecvQueue, &myobja); myInMsgObj.join(); myOutnMsgObj.join(); cout << "I live China!" << endl; //最后执行这句,整个进程退出 system("pause"); return 0; }
2.2 std::try_to_lock
std::try_to_lock()
- 尝试用
mutex
的lock()
去锁定这个mutex
。但如果没有锁定成功,也会立即返回,并不会阻塞到那里。 - 用这个
try_to_lock
的前提是你自己不能先去lock
。
示例代码:
#include <iostream> #include <thread> #include <vector> #include <list> #include <mutex> using namespace std; class A { public: //把收到的消息(玩家命令)入到一个队列的线程 void inMsgRecvQueue() { for (int i = 0; i < 10000; i++) { cout << "inMsgRecvQueue()执行,插入一个元素:" << i << endl; std::unique_lock<std::mutex> sbguard1(my_mutex,std::try_to_lock); if (sbguard1.owns_lock()) { //拿到了锁 msgRecvQueue.push_back(i); //...... } else { cout << "inMsgRecvQueue()执行,但没有拿到锁,只能干点别的事" << i << endl; } } return; } bool outMsgLULProc(int& command) { std::unique_lock<std::mutex> sbguard1(my_mutex); std::chrono::milliseconds dura(20000); //1s = 1000ms,20000ms = 20s std::this_thread::sleep_for(dura); if (!msgRecvQueue.empty()) { //消息不为空 command = msgRecvQueue.front(); //返回第一个元素,但不检查元素是否存在 msgRecvQueue.pop_front(); //移除第一个元素,但不返回 return true; } return false; } //把数据从消息队列中取出的线程 void outMsgRecvQueue() { int command = 0; for (int i = 0; i < 10000; i++) { bool result = outMsgLULProc(command); if (result == true) { cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl; //接下来就考虑处理数据...... } else { //消息队列为空 cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl; } } } private: list<int> msgRecvQueue; //容器,专门用于代表玩家给咱们发送过来的命令 mutex my_mutex; //创建了一个互斥量 }; int main() { A myobja; thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja); thread myInMsgObj(&A::inMsgRecvQueue, &myobja); myInMsgObj.join(); myOutnMsgObj.join(); cout << "I live China!" << endl; //最后执行这句,整个进程退出 system("pause"); return 0; }
2.3 std::defer_lock
std::defer_lock
- 用
std::defer_lock
的前提是,你不嗯能够自己先lock()
,否则会报异常。 defer_lock
的意思就是:并没有给mutex
加锁,初始化了一个没有加锁的mutex
。
3、unique_lock的成员函数
3.1 lock(),unlock()
示例代码:
#include <iostream> #include <thread> #include <vector> #include <list> #include <mutex> using namespace std; class A { public: //把收到的消息(玩家命令)入到一个队列的线程 void inMsgRecvQueue() { for (int i = 0; i < 10000; i++) { std::unique_lock<std::mutex> sbguard1(my_mutex, std::defer_lock); //没有加锁的my_mutex sbguard1.lock(); //不用自己unlock //因为有一些非共享的代码要处理,所以要unlock() sbguard1.unlock(); //这里处理一些非共享代码 //处理完非共享代码后,继续上锁 sbguard1.lock(); //这里处理共享代码 //拿到了锁 msgRecvQueue.push_back(i); //...... sbguard1.unlock(); //画蛇添足,但也可以 } return; } bool outMsgLULProc(int& command) { std::unique_lock<std::mutex> sbguard1(my_mutex); //std::chrono::milliseconds dura(20000); //1s = 1000ms,20000ms = 20s //std::this_thread::sleep_for(dura); if (!msgRecvQueue.empty()) { //消息不为空 command = msgRecvQueue.front(); //返回第一个元素,但不检查元素是否存在 msgRecvQueue.pop_front(); //移除第一个元素,但不返回 return true; } return false; } //把数据从消息队列中取出的线程 void outMsgRecvQueue() { int command = 0; for (int i = 0; i < 10000; i++) { bool result = outMsgLULProc(command); if (result == true) { cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl; //接下来就考虑处理数据...... } else { //消息队列为空 cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl; } } } private: list<int> msgRecvQueue; //容器,专门用于代表玩家给咱们发送过来的命令 mutex my_mutex; //创建了一个互斥量 }; int main() { A myobja; thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja); thread myInMsgObj(&A::inMsgRecvQueue, &myobja); myInMsgObj.join(); myOutnMsgObj.join(); cout << "I live China!" << endl; //最后执行这句,整个进程退出 system("pause"); return 0; }
3.2 try_lock())
try_lock()
- 尝试给互斥量加锁,如果拿不到锁,则返回
false
,如果拿到了锁,返回true
,这个函数不阻塞。
示例代码:
#include <iostream> #include <thread> #include <vector> #include <list> #include <mutex> using namespace std; class A { public: //把收到的消息(玩家命令)入到一个队列的线程 void inMsgRecvQueue() { for (int i = 0; i < 10000; i++) { std::unique_lock<std::mutex> sbguard1(my_mutex, std::defer_lock); //没有加锁的my_mutex if (sbguard1.try_lock() == true) //返回true表示拿到锁了 { msgRecvQueue.push_back(i); cout << "inMsgRecvQueue()执行,插入一个元素 " << i << endl; //...... } else { cout << "inMsgRecvQueue()执行,但没有拿到锁,只能干点别的事" << i << endl; } } return; } bool outMsgLULProc(int& command) { std::unique_lock<std::mutex> sbguard1(my_mutex); std::chrono::milliseconds dura(500); //1s = 1000ms,500ms = 0.5s std::this_thread::sleep_for(dura); if (!msgRecvQueue.empty()) { //消息不为空 command = msgRecvQueue.front(); //返回第一个元素,但不检查元素是否存在 msgRecvQueue.pop_front(); //移除第一个元素,但不返回 return true; } return false; } //把数据从消息队列中取出的线程 void outMsgRecvQueue() { int command = 0; for (int i = 0; i < 10000; i++) { bool result = outMsgLULProc(command); if (result == true) { cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl; //接下来就考虑处理数据...... } else { //消息队列为空 cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl; } } } private: list<int> msgRecvQueue; //容器,专门用于代表玩家给咱们发送过来的命令 mutex my_mutex; //创建了一个互斥量 }; int main() { A myobja; thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja); thread myInMsgObj(&A::inMsgRecvQueue, &myobja); myInMsgObj.join(); myOutnMsgObj.join(); cout << "I live China!" << endl; //最后执行这句,整个进程退出 system("pause"); return 0; }
3.3 release()
release()
- 返回它所管理的
mutex
对象指针,并释放所有权;也就是说,这个unique_lock
和mutex
不再有关系。 - 严格区分
unlock()
和release()
的区别,不要混淆。 - 如果原来的
mutex
对象处于加锁状态,程序员有责任接管过来并负责解锁。
示例代码:
#include <iostream> #include <thread> #include <vector> #include <list> #include <mutex> using namespace std; class A { public: //把收到的消息(玩家命令)入到一个队列的线程 void inMsgRecvQueue() { for (int i = 0; i < 10000; i++) { std::unique_lock<std::mutex> sbguard1(my_mutex); //没有加锁的my_mutex std::mutex* ptx = sbguard1.release(); //现在你有责任自己解锁这个my_mutex msgRecvQueue.push_back(i); cout << "inMsgRecvQueue()执行,插入一个元素 " << i << endl; ptx->unlock(); //自己负责mutex的unlock() } return; } bool outMsgLULProc(int& command) { std::unique_lock<std::mutex> sbguard1(my_mutex); std::chrono::milliseconds dura(500); //1s = 1000ms,500ms = 0.5s std::this_thread::sleep_for(dura); if (!msgRecvQueue.empty()) { //消息不为空 command = msgRecvQueue.front(); //返回第一个元素,但不检查元素是否存在 msgRecvQueue.pop_front(); //移除第一个元素,但不返回 return true; } return false; } //把数据从消息队列中取出的线程 void outMsgRecvQueue() { int command = 0; for (int i = 0; i < 10000; i++) { bool result = outMsgLULProc(command); if (result == true) { cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl; //接下来就考虑处理数据...... } else { //消息队列为空 cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl; } } } private: list<int> msgRecvQueue; //容器,专门用于代表玩家给咱们发送过来的命令 mutex my_mutex; //创建了一个互斥量 }; int main() { A myobja; thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja); thread myInMsgObj(&A::inMsgRecvQueue, &myobja); myInMsgObj.join(); myOutnMsgObj.join(); cout << "I live China!" << endl; //最后执行这句,整个进程退出 system("pause"); return 0; }
为什么有时候需要
unlock()
?
- 因为
lock()
锁住的代码段越少,执行越快,整个程序运行效率越高。- 锁头锁住的代码的多少称为锁的粒度,粒度一般用粗细来描述。
- 锁住的代码少,这个粒度叫细,执行效率高。
- 锁住的代码多,粒度叫粗,执行效率就低。
- 要学会尽量选择合适粒度的代码进行保护,力度太细,可能漏掉共享数据的保护,粒度太粗,影响效率。
- 选择合适的粒度,是高级程序员的能力和实力的体现。
4、unique_lock所有权的传递
std::unique_lock<std::mutex> sbguard1(my_mutex)
sbguard1
拥有my_mutex
的所有权sbguard1
可以把自己对mutex(my_mutex)
的所有权转移给其他的unique_lock
对象。unique_lock
对象对mutex
的所有权可以转移,但不能复制。
4.1 std::move
第一种std::move
所有权传递示例代码:
#include <iostream> #include <thread> #include <vector> #include <list> #include <mutex> using namespace std; class A { public: //把收到的消息(玩家命令)入到一个队列的线程 void inMsgRecvQueue() { for (int i = 0; i < 10000; i++) { std::unique_lock<std::mutex> sbguard1(my_mutex); //没有加锁的my_mutex std::unique_lock<std::mutex> sbguard2(std::move(sbguard1)); msgRecvQueue.push_back(i); cout << "inMsgRecvQueue()执行,插入一个元素 " << i << endl; } return; } bool outMsgLULProc(int& command) { std::unique_lock<std::mutex> sbguard1(my_mutex); //std::chrono::milliseconds dura(500); //1s = 1000ms,500ms = 0.5s //std::this_thread::sleep_for(dura); if (!msgRecvQueue.empty()) { //消息不为空 command = msgRecvQueue.front(); //返回第一个元素,但不检查元素是否存在 msgRecvQueue.pop_front(); //移除第一个元素,但不返回 return true; } return false; } //把数据从消息队列中取出的线程 void outMsgRecvQueue() { int command = 0; for (int i = 0; i < 10000; i++) { bool result = outMsgLULProc(command); if (result == true) { cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl; //接下来就考虑处理数据...... } else { //消息队列为空 cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl; } } } private: list<int> msgRecvQueue; //容器,专门用于代表玩家给咱们发送过来的命令 mutex my_mutex; //创建了一个互斥量 }; int main() { A myobja; thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja); thread myInMsgObj(&A::inMsgRecvQueue, &myobja); myInMsgObj.join(); myOutnMsgObj.join(); cout << "I live China!" << endl; //最后执行这句,整个进程退出 system("pause"); return 0; }
4.2 return std::unique_lockstd::mutex
第二种return std::unique_lock<std::mutex>
所有权传递示例代码:
#include <iostream> #include <thread> #include <vector> #include <list> #include <mutex> using namespace std; class A { public: std::unique_lock<std::mutex> rtn_unique_lock() { std::unique_lock<std::mutex> tmpguard(my_mutex); return tmpguard; //从函数返回一个局部的unique_lock对象是可以的 //返回这种局部对象tmp_guard会导致系统生成临时unique_lock对象,并调用unique_lock的移动构造函数 } //把收到的消息(玩家命令)入到一个队列的线程 void inMsgRecvQueue() { for (int i = 0; i < 10000; i++) { std::unique_lock<std::mutex> tmpguard1 = rtn_unique_lock(); msgRecvQueue.push_back(i); cout << "inMsgRecvQueue()执行,插入一个元素 " << i << endl; } return; } bool outMsgLULProc(int& command) { std::unique_lock<std::mutex> sbguard1(my_mutex); //std::chrono::milliseconds dura(500); //1s = 1000ms,500ms = 0.5s //std::this_thread::sleep_for(dura); if (!msgRecvQueue.empty()) { //消息不为空 command = msgRecvQueue.front(); //返回第一个元素,但不检查元素是否存在 msgRecvQueue.pop_front(); //移除第一个元素,但不返回 return true; } return false; } //把数据从消息队列中取出的线程 void outMsgRecvQueue() { int command = 0; for (int i = 0; i < 10000; i++) { bool result = outMsgLULProc(command); if (result == true) { cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl; //接下来就考虑处理数据...... } else { //消息队列为空 cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl; } } } private: list<int> msgRecvQueue; //容器,专门用于代表玩家给咱们发送过来的命令 mutex my_mutex; //创建了一个互斥量 }; int main() { A myobja; thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja); thread myInMsgObj(&A::inMsgRecvQueue, &myobja); myInMsgObj.join(); myOutnMsgObj.join(); cout << "I live China!" << endl; //最后执行这句,整个进程退出 system("pause"); return 0; }
注:本人学习c++多线程视频地址:C++多线程学习地址
这篇关于C++11并发与多线程笔记(6)unique_lock详解的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享