C++11多线程互斥量用法(二)

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对象智能移动,并不能复制。
在这里插入图片描述
这样的操作是存在错误的。
在这里插入图片描述

  • 21
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值