一、unique_lock取代lock_guard
unique_lock是一个类模板,一般项目之中推荐使用lock_guard。lock_guard取代了lock()与unlock()。unique_lock比lock_guard灵活很多,但是效率上会差一点,内存占用更多。
//
// Created by yangjq on 22-7-7.
//
#include <iostream>
#include <thread>
#include <vector>
#include <list>
#include <mutex>
using namespace std;
class A{
public:
//把玩家命令收到一个队列的线程
void inMsgRecvQueue(){
for(int i = 0; i < 100000; ++i){
cout << "inMsgRecvQueue()执行,插入一个元素" << i << endl;
//std::lock_guard<std::mutex> sbguard(my_mutex1);
std::unique_lock<std::mutex> sbguard(my_mutex1);
msgRecvQueue.push_back(i);//数字i就是玩家收到的命令
}
}
bool outMsgLULProc(int &command){
//std::lock_guard<std::mutex> sbguard(my_mutex1);
std::unique_lock<std::mutex> sbguard(my_mutex1);
if(!msgRecvQueue.empty()) {
//消息不为空
command = msgRecvQueue.front();//返回前面的元素
msgRecvQueue.pop_front();
//处理数据...
return true;
}
return false;
}
//数据从消息队列中取出的线程
void outMsgRecvQueue(){
int command = 0;
for(int i = 0; i < 100000; ++i){
bool result = outMsgLULProc(command);
if(result){
cout << "outMsgRecvQueue()执行,删除一个元素!" << endl;
}
else{
cout << "outMsgRecvQueue()执行,消息队列为空" << i << endl;
}
}
}
private:
//容器,专门用于存储玩家发送过来的命令
std::list<int> msgRecvQueue;
std::mutex my_mutex1;
std::mutex my_mutex2;
};
int main()
{
//用成员函数作为线程函数的方法来写线程
A myobja;
std::thread myOutMsgObj(&A::outMsgRecvQueue, &myobja);
std::thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
myInMsgObj.join();
myOutMsgObj.join();
cout << "main end !" << endl;
return 0;
}
二、unique_lock的第二参数
std::adopt_lock : 表示这个互斥量已经被lock了,因此使用这个参数时,必须把互斥量提前lock,不然程序会出现异常。简单来说就是通知构造类对象时构造函数不需要在lock了,只需要记得析构函数执行时完成unlock即可。这个参数在lock_guard里面也可用。具体使用看下面代码,为什么搞这么复杂呢?因为应用场景复杂,不同的应用场景使用不同的方式会更适合。
std::unique_lock<std::mutex> sbguard(my_mutex1);
等价于
my_mutex1.lock();
std::unique_lock<std::mutex> sbguard(my_mutex1, std::adopt_lock);
std::try_to_lock : 前提是不能先自己lock()。std::try_to_lock是尝试用mutex的lock()去锁定这个mutex,但如果没有锁定成功,会立即返回,并不会阻塞在那里。在没拿到锁的时间可以执行别的程序,不会卡在那里浪费时间。
std::unique_lock<std::mutex> sbguard(my_mutex1, std::try_to_lock);
if(sbguard.owns_lock()){
//拿到了锁
...执行语句
}
else{
//没拿到锁
...执行语句
}
std::defer_lock : 前提是不能先自己lock()。defer_lock的意思就是没有给mutex加锁,即初始化了一个没有加锁的mutex。
std::unique_lock<std::mutex> sbguard(my_mutex1);
等价于
std::unique_lock<std::mutex> sbguard(my_mutex1, std::defer_lock);
sbguard.lock();//注意时sbguard加锁,而不是my_mutex1
二、unique_lock的成员函数
上面defer_lock已经提出了unique_lock的一个成员函数lock(),其实unique_lock也有unlock()。应用场景如下:
std::unique_lock<std::mutex> sbguard(my_mutex1, std::defer_lock);
sbguard.lock();//注意时sbguard加锁,而不是my_mutex1
...处理共享数据代码
sbguard.unlock();
...处理其他代码
sbguard.lock();
...处理共享数据代码
//至函数结束,unique_lock会自动再unlock
另外还有成员函数try_lock() : 尝试给互斥量加锁,如果拿不到的话,则返回false,如果拿到了锁,返回true,这个函数不阻塞。和第二参数try_to_lock有点像。
std::unique_lock<std::mutex> sbguard(my_mutex1);
if(sbguard.try_lock()){
//拿到了锁
...执行代码
}
else{
//没拿到锁
...执行代码
}
最后介绍一下release() : 返回它所管理的mutex对象指针,并释放所有权;也就是说,这个unique_lock和mutex不再有关系。
std::unique_lock<std::mutex> sbguard(my_mutex1);
等价于
std::unique_lock<std::mutex> sbguard(my_mutex1);
std::mutex *ptx = sbguard.release();//解除关系,返回的是my_mutex1的指针。
...执行语句
ptx->unlock();//解除了unique_lock与my_mutex1的关系,那么就要自己unlock()。
总的来说,unique_lock进行lock和unlock更加的灵活,因此可以尽可能的控制需要锁住多少代码,更加灵活。
四、unique_lock所有权的传递
std::unique_lock<std::mutex> sbguard(my_mutex1);
所有权概念:对于上面的一行代码,sbguard拥有my_mutex1的所有权。
因此unique_lock所有权的传递就是sbguard对于my_mutex1的所有权是可以传递的,但是不能复制。传递方式如下
std::unique_lock<std::mutex> sbguard(my_mutex1);
//sbguard2接管了sbguard对my_mutex1的所有权
std::unique_lock<std::mutex> sbguard2(std::move(sbguard));//进行转移