unique_lock
lock_guard
(推荐使用);lock_guard
取代了mutex
的lock()
和unlock()
;unique_lock
比lock_guard
灵活很多;效率上差一点,内存占用多一点。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; }
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; }
std::defer_lock
std::defer_lock
的前提是,你不嗯能够自己先lock()
,否则会报异常。defer_lock
的意思就是:并没有给mutex
加锁,初始化了一个没有加锁的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, 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; }
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; }
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()
锁住的代码段越少,执行越快,整个程序运行效率越高。- 锁头锁住的代码的多少称为锁的粒度,粒度一般用粗细来描述。
- 锁住的代码少,这个粒度叫细,执行效率高。
- 锁住的代码多,粒度叫粗,执行效率就低。
- 要学会尽量选择合适粒度的代码进行保护,力度太细,可能漏掉共享数据的保护,粒度太粗,影响效率。
- 选择合适的粒度,是高级程序员的能力和实力的体现。
std::unique_lock<std::mutex> sbguard1(my_mutex)
sbguard1
拥有my_mutex
的所有权sbguard1
可以把自己对mutex(my_mutex)
的所有权转移给其他的unique_lock
对象。unique_lock
对象对mutex
的所有权可以转移,但不能复制。第一种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; }
第二种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++多线程学习地址