6-6 unique_lock详解

class A
{
public:
	std::unique_lock<std::mutex> rtn_unique_lock()
	{
		std::unique_lock<std::mutex> tmpguard(my_mutex1);
		return tmpguard;//从函数返回一个局部的unique_lock对象是可以的。
		//返回这种局部对象tmpguard会导致系统生成临时unique_lock对象,并调用unique_lock的移动构造函数
	}
	//把收到的消息(玩家命令)入到一个队列的线程
	void inMsgRecvQueue()
	{
		for(int i = 0;i<100000;++i)
		{
			cout<<"inMsgRecvQueue()执行,插入一个元素"<<i<<endl;
			//my_mutex2.lock();		//死锁,这边先2后1,下边先1后2
			//my_mutex1.lock();	//实际工程中这两个锁头不一定挨着,们需要保护不同的数据共享块;
			//std::lock(my_mutex1,my_mutex2);	//相当于每个互斥量都调用了.lock();
			//std::lock_guard<std::mutex> sbguard(my_mutex1,std::adopt_lock);	//这样能省略自己unlock的步骤
			//std::lock_guard<std::mutex> sbguard(my_mutex2,std::adopt_lock);
			//std::unique_lock<std::mutex> sbguard1(my_mutex1, std::try_to_lock);
			//if(sbguard1.owns_lock()
			//{}else[]
			//std::unique_lock<std::mutex> sbguard1(my_mutex1,std::defer_lock);//没有加锁的my_mutex1
			//sbguard1.lock();//用了defer_lock后要自己加锁解锁
			//sbguard1.unlock();
			//if(sbguard1.try_lock() == true)	//表示尝试加锁成功
			//{
			//}
			//else
			//{
				//没拿到锁会干别的事,不会卡住
			//}
			//std::unique_lock<std::mutex> sbguard1(my_mutex1);
			//std::unique_lock<std::mutex> sbguard2(sbguard1);//复制所有权是非法的
			//std::unique_lock<std::mutex> sbguard2(std::move(sbguard1));//移动语义,现在相当于sbguard2和my_mutex1绑定到一起了,现在sbguard1指向空,sbguard2指向my_mutex1
			//std::mutex *ptx = sbguard1.release();	//现在你有责任自己解锁这个my_mutex1;
			//ptx->unlock();	//ptx相当于my_mutex1
			std::unique_lock<std::mutex> sbguard1 = rtn_unique_lock();
			msgRecvQueue.push_back(i);//假设这个数字i就是我收到的命令,我直接弄到消息队列里边来;
			//my_mutex.unlock();
		}
		return;
	}
	bool out MsgLULProc(int &command)
	{
		std::lock_guard<std::mutex> sbguard(my_mutex);//sbguard是随便起的对象名
		//lock_guard构造函数里执行了mutex::lock(),析构函数里执行了mutex::unlock();
		//my_mutex1.lock();		//死锁
		//my_mutex2.lock();
		//std::lock(my_mutex1,my_mutex2);	//相当于每个互斥量都调用了.lock();
		if(!msgRecvQueue.empty())
		{
			//消息不为空
			int command = msgRecvQueue.front();	//返回第一个元素,但不检查元素是否存在
			msgRecvQueue.pop_front();	//移除第一个元素
			//my_mutex.unlock();	//一定不能忘记return前的解锁
			return true;
		}
		//my_mutex.unlock();
		return false;
	}
	//把数据从消息队列中取出的线程
	void outMsgRecvQueue()
	{
		int command = 0;
		for(int i = 0;i<10000;i++)
		{
			bool result = outMsgLULProc(command);
			if(result == true)
			{
				cout<<"out执行了,取出一个元素"<<command<<endl;
			}
			else
			{
				cout<<"消息队列为空"<<i<<endl;
			}
			/*if(!msgRecvQueue.empty())
			{
				//消息不为空
				int command = msgRecvQueue.front();//返回第一个元素,但不检查元素是否存在;
				msgRecvQueue.pop_front();	//移除第一个元素,但不返回;
				//这里就考虑处理数据....
				//....
			}
			else
			{
				//消息队列为空
				cout<<"消息队列为空"<<i<<endl;
			}*/
		}
		cout<<"end"<<endl;
	}
private:
std::list<int> msgRecvQueue;//容器,专门用于代表玩家给咱们发送过来的命令。
std::mutex my_mutex;	//创建了一个互斥量
};

int main()
{
	A myobja;
	std::thread myOutnMsgObj(&A::outMsgRecvQueue, &myobja);//第二个参数是引用才能保证用的是同一个对象
	std::thread myInMsgObj(&A::inMsgRecvQueue, &myobja);//
	myOutnMsgObj.join();
	myInMsgObj.join();
}

一:unique_lock取代lock_guard

unique_lock是个类模板,工作中,一般lock_guard(推荐使用);lock_guard取代了mutex的lock()和unlock();
unique_lock比lock_guard灵活很多;效率上差一点,内存占用多一点。

二:unique_lock的第二个参数

lock_guard可以带第二个参数:
std::lock_guardstd::mutex sbguard1(my_mutex1,std::adopt_lock); //adopt_lock标记作用;
2.1、std::adopt_lock:表示这个互斥量已经被lock了(你必须要把互斥量提前lock了,否则会报异常)
std::adopt_lock标记的效果就是“假设调用方线程已经拥有了互斥的所有权(已经lock()成功了);通知lock_guard不需要在构造函数中lock这个互斥量了;
unique_lock也可以带std::adopt_lock标记,含义相同,就是不希望在unique_lock()的构造函数中lock这个mutex。
用这个adopt_lock的前提是,你需要自己先把mutex先lock上;
2.2、std::try_to_lock
我们会尝试用mutex的lock()去锁定这个mutex,但如果没有锁定成功,我也会立即返回,并不会阻塞在那里;
用这个try_to_lock的前提是你自己不能先去lock。
2.3、std::defer_lock
用这个defer_lock的前提是你不能自己先lock,否则会报异常。
defer_lock的意思就是并没有给mutex加锁:初始化了一个没有加锁的mutex。
我们借着defer_lock的话题,来介绍一些unique_lock的重要成员函数

三:unique_lock的成员函数

3.1、lock(),加锁
3.2、unlock(),解锁
3.3、try_lock(),尝试给互斥量加锁,如果拿不到锁,则返回false,如果拿到了锁,返回true,这个函数不阻塞的;
3.4、release(),返回它所管理的mutex对象指针,并释放所有权;也就是说,这个unique_lock和mutex不再有关系。
严格区分unlock()和release()的区别,不要混淆。
如果原来mutex对象处于加锁状态,你有责任接管过来并负责解锁。(release返回的是原始mutex的指针)
为什么有时候需要unlock():因为你lock锁住的代码段越少,执行越快,整个程序运行效率越高。
有人也把锁头锁住的代码多少称为锁的粒度,粒度一般用粗细来描述;
a、锁住的代码少,这个粒度叫细。执行效率高;
b、锁住的代码多,粒度叫粗,那执行效率就低;
要学会尽量选择合适粒度的代码进行保护,粒度太细,可能漏掉共享数据的保护,粒度太粗,影响效率。
选择合适的粒度,是高级程序员实力的体现。

四:unique_lock所有权的传递,mutex

std::unique_lockstd::mutex sbguard1(my_mutex1):所有权概念
sbguard1拥有my_mutex1的所有权
sbguard1可以把自己对mutex(my_mutex1)的所有权转移给其他的unique_lock对象;
所以,unique_lock对象这个mutex的所有权是属于可以转移的,但是不能复制。

a、std::move
b、return unique_lock

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值