C++11多线程互斥量用法(二)
五、unique_lock(类模板)取代lock_guard
5.1、在缺省参数的情况下
#include <iostream>
#include <thread>
#include <list>
#include <mutex>
class A
{
public:
void inMsgRecvList()
{
for (int i = 0; i < 100000; ++i)
{
std::cout << "inMsgRecvList()执行插入一个元素" << std::endl;
//加{}提前结束myguard的生命周期就可以减少加锁的时间
{
{
std::unique_lock<std::mutex> myGruad1(myMutex1);
msgRecvList.push_back(i);
}
}
}
return;
}
bool outMsgRecvMutex(int& command)
{
std::unique_lock<std::mutex> myGruad1(myMutex1);
if (!msgRecvList.empty()) //注意判断是否为空也是读操作
{
command = msgRecvList.front(); //front只是读取数值并没有删除
msgRecvList.pop_front();
return true;
}
return false;
}
void outMsgRecvList()
{
int command = 0;
for (int i = 0; i < 100000; ++i)
{
bool result = outMsgRecvMutex(command);
if (result)
{
std::cout << "outMsgRecvList()执行取出一个命令" << std::endl;
}
else
{
std::cout << "outMsgRecvList()执行,但消息队列为空" << i<<std::endl;
}
}
std::cout << "end" << std::endl;
}
protected:
std::list<int> msgRecvList; //保存接收到的消息
std::mutex myMutex1;
};
int main()
{
A obja;
std::thread myOutMsgObj(&A::outMsgRecvList, std::ref(obja));
std::thread myInMsgObj(&A::inMsgRecvList, std::ref(obja));
myOutMsgObj.join();
myInMsgObj.join();
return 0;
}
在unique_lock缺省参数的情况下,unique_lock和lock_guard的作用相同,但是效率差一点,内存占多一点,但是在存在参数的情况下,unique_lock更加的灵活。
5.2、加入参数
5.2.1、std::adopt_lock
std::adopt_lock标记的效果:表示这个互斥量已经被锁,通知构造函数不需要在执行lock这个互斥量了。std::unique_lock和lock_gruad用这个参数效果相同。
std::adopt_lock需要自己先lock
void inMsgRecvList()
{
for (int i = 0; i < 100000; ++i)
{
std::cout << "inMsgRecvList()执行插入一个元素" << std::endl;
//加{}提前结束myguard的生命周期就可以减少加锁的时间
{
{
myMutex1.lock();
std::unique_lock<std::mutex> myGruad1(myMutex1,std::adopt_lock);
\\作用就是在myGruad1的生命周期结束的时候调用析构函数执行unlock
msgRecvList.push_back(i);
}
}
}
return;
}
5.2.2、std::try_to_lock
unique_lock的参数
作用是尝试去lock互斥量,如果没有锁定成功,会立即返回,并不会阻塞在那,一直尝试加锁。(使用的前提是你不能先对同一互斥量加锁,否则会产生死锁)。
std::try_to_lock不能自己先lock
std::unique_lock<std::mutex> myGruad1(myMutex1,std::try_to_lock);
利用myGruad1.owns_lock()判断是否加锁,成功返回true,失败返回false
void inMsgRecvList()
{
for (int i = 0; i < 100000; ++i)
{
//加{}提前结束myguard的生命周期就可以减少加锁的时间
{
std::unique_lock<std::mutex> myGruad1(myMutex1,std::try_to_lock);
if (myGruad1.owns_lock())
{
std::cout << "inMsgRecvList()执行成功上锁" << std::endl;
std::cout << "inMsgRecvList()执行插入一个元素" << std::endl;
msgRecvList.push_back(i);
}
else
{
std::cout << "inMsgRecvList()执行没拿到锁" << std::endl;
}
}
}
return;
}
bool outMsgRecvMutex(int& command)
{
std::unique_lock<std::mutex> myGruad1(myMutex1);
std::this_thread::sleep_for(std::chrono::milliseconds(20)); //让程序停止20ms
if (!msgRecvList.empty()) //注意判断是否为空也是读操作
{
command = msgRecvList.front(); //front只是读取数值并没有删除
msgRecvList.pop_front();
return true;
}
return false;
}
就想这样在第二个线程加锁睡眠20ms的时间内,第一个线程并不能成功加锁,就会返回false,并不会阻塞第一个线程。
5.2.3、std::defer_lock
std::defer_lock不能自己先lock
5.2.3.1、unique_lock的成员函数lock()和unlock()
void inMsgRecvList()
{
for (int i = 0; i < 100000; ++i)
{
//加{}提前结束myguard的生命周期就可以减少加锁的时间
{
std::unique_lock<std::mutex> myGruad1(myMutex1, std::defer_lock);
//在生命周期结束的时候会自动判断是否解锁,如果未解锁,会执行析构函数解锁
//如果已经解锁,则不会再次解锁
myGruad1.lock();
//处理共享代码
myGruad1.unlock();
//处理非共享代码
myGruad1.lock();
//操作共享代码
std::cout << "inMsgRecvList()执行插入一个元素" << std::endl;
msgRecvList.push_back(i);
}
}
return;
}
可以看到允许在处理非共享数据的时候进行解锁,需要处理共享数据的时候再加锁,并且再生命周期结束的时候,若未解锁,会自动解锁。
5.2.3.2、unique_lock的成员函数try_lock()
void inMsgRecvList()
{
for (int i = 0; i < 100000; ++i)
{
//加{}提前结束myguard的生命周期就可以减少加锁的时间
{
std::unique_lock<std::mutex> myGruad1(myMutex1, std::defer_lock);
//在生命周期结束的时候会自动判断是否解锁,如果未解锁,会执行析构函数解锁
//如果已经解锁,则不会再次解锁
if (myGruad1.try_lock())
{
// 表示已经拿到锁了
std::cout << "inMsgRecvList()执行插入一个元素" << std::endl;
msgRecvList.push_back(i);
}
else
{
//表示没拿到锁
}
}
}
return;
}
类似于std::try_to_lock不会阻塞,尝试加锁,返回值表示结果
5.2.3.3、unique_lock的成员函数release()
作用:解除unique_lock和mutex的绑定关系
void inMsgRecvList()
{
for (int i = 0; i < 100000; ++i)
{
//加{}提前结束myguard的生命周期就可以减少加锁的时间
{
std::unique_lock<std::mutex> myGruad1(myMutex1, std::defer_lock);
//在生命周期结束的时候会自动判断是否解锁,如果未解锁,会执行析构函数解锁
//如果已经解锁,则不会再次解锁
myGruad1.lock();
std::mutex* ptx = myGruad1.release(); //解除绑定关系并且返回mutex的指针
std::cout << "inMsgRecvList()执行插入一个元素" << std::endl;
msgRecvList.push_back(i);
ptx->unlock();//现在就需要手动的进行mutex的解锁
}
}
return;
}
在未执行release()之前可以看到unique_lock保存这mutex的地址
在执行了release()之后,unique_lock不保存mutex的地址了,release()返回了mutex的地址。
六、unique_lock的所有权转移
类似于unique_ptr对象智能移动,并不能复制。
这样的操作是存在错误的。