C++语法学习笔记四十六:互斥量概念、用法、死锁演示及解决详解

实例代码:

// 
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <list>
#include <map>
#include <string>

using namespace std;


class A{
public:
	//把收到的消息 (玩家命令) 入到一个队列的线程

	void inMsgRecvQueue(){
		for (int i = 0; i < 10000; ++i) {
			cout << "inMsgRecvQueue() 执行,插入一个元素" << i << endl;

			//std::lock_guard<std::mutex> sbguar(my_mutex);   
			//std::lock(my_mutex1, my_mutex2); //相当于每个互斥量都调用了.lock();

			my_mutex.lock();
			msgRecvQueue.push_back(i); //假设这个数字i 就是我收到的命令, 我直接弄到消息队列里边来;
			my_mutex.unlock();
		}
	}

	bool outMsgLULProc(int &command){

		//std::lock_guard<std::mutex> sbguar(my_mutex);
							//lock_guard构造函数里执行了mutex::lock();
							//lock_guard析构函数里执行了mutex::unlock();

		my_mutex.lock();
		if (!msgRecvQueue.empty()) {
			//消息不为空
			command = msgRecvQueue.front(); //返回第一个元素,但不检查元素是否存在;
			msgRecvQueue.pop_front(); //移除第一个元素,但不返回
			my_mutex.unlock();
			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 << "outMsgRecvQueue()执行,取出一个元素 " << command << endl;
				//可以考虑进行命令(数据)处理
				//.....
			}
			else{
				//消息队列为空
				cout << "outMsgRecvQueue() 执行,但目前消息队列中为空" << i << endl;
			}
		}
		cout << "end" << endl;
	}

private:

	std::list<int> msgRecvQueue; //容器, 专门用于代表玩家给咱们发生过来的命令
	std::mutex my_mutex; //创建了一个互斥量

};


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


	//保护共享大数据,操作时,某个线程 用代码把共享数据锁住、操作数据、解锁,
					//其他想操作共享数据的线程必须等待解锁,锁定住,操作,解锁。

	//"互斥量"
	//一:互斥量(mutex)的基本概念
	//互斥量是个类对象。理解成一把锁,多个线程尝试用lock()成员函数来加锁这把锁头,只有一个线程能锁定成功
		//(成功的标志是lock()函数返回)如果没锁成功,那么流程卡在lock()这里不断的尝试去锁这把锁头;
	//互斥量使用要小心,保护数据不多也不少,少了,没达到保护效果,多了,影响效率;
	
	//二:互斥量的用法
		//(2.1) lock() , unlock()
		//步骤:先lock(), 操作共享数据,unlock();
		//lock()和unlock()要成对使用,有lock必然要有unlock,每调用一次lock(),必然应该调用一次unlock();
		//不应该也不允许调用1次lock()却调用了2次unlock(),也不允许调用2次lock却调用1次unlock(),这些非对称
		//数量的调用都会导致代码不稳定甚至崩溃。有lock, 忘记unlock的问题,非常难排查;
		//为了防止大家忘记unlock(),引入了一个叫std::lock_guard的类模板:你忘记unlock不要紧,我替你unlock();
		//学习过智能指针(unique_ptr<>) : 你忘记释放内存不要紧,我给你释放;

		//(2.2) std::lock_guard 类模板:直接取代lock() 和unlock();也就是说, 你用了lock_guard之后,再不能使用lock()
		//unlock()了;


	//三:死锁
	//张三 : 站在北京 等李四 ,不挪窝;
	//李四 : 站在深圳 等张三 ,不挪窝;
	//C++中:
	//比如我有两把锁(死锁这个问题 是由至少两个锁头也就是两个互斥量才能产生);金锁(jinlock) 银锁(yinlock)
	//两个线程 A,B
	//(1) 线程A执行的时候,这个线程先锁金锁,把金锁lock()成功了,然后它去lock银锁。。。
	//出现了上下文切换
	//(2) 线程B执行了,这个线程先锁银锁,因为银锁还没有被锁,所以银锁会lock()成功,线程B要去lock金锁。。。
	//此时此刻,死锁就产生了;
	//(3) 线程A因为拿不到银锁头,流程走不下去(所有后边代码有解锁金锁锁头的但是流程走不下去,所以金锁头解不开)
	//(4) 线程B因为拿不到金锁头,流程走不下去(所有后边代码有解锁银锁锁头的但是流程走不下去,所以银锁头解不开)
	//大家都晾在这里,你等我,我等你;


		//(3.1) 死锁演示
		//(3.2) 死锁的一般解决方案
		//只要保证这两个互斥量上锁的顺序一致就不会死锁。

		//(3.3) std::lock() 函数模板: 用来处理多个互斥量的时候才出场
		//能力:一次锁住两个或者两个以上的互斥量(至少两个,多了不限,1个不行);
		//它不存在这种因为再多个线程中 因为锁的顺序问题导致死锁的风险问题;
		//std::lock():如果互斥量中有一个没锁住,它就在那里等着,等所有互斥量都锁住,它才能往下走(返回);
		//那么两个互斥量都锁住,要么两个互斥量都没锁住。如果只锁了一个,另外一个没锁成功,则它立即把已经锁住的解锁。

		//(3.4) std::lock guard 的std::adopt_lock参数
		//std::adopt_lock是个结构体对象,起一个标记作用:作用就是表示这个互斥量已经lock(),
		//不需要再std::lock_guard<std::mutext>构造函数里 再面对对象进行再次lock()了;

		//总结: std::lock();一次锁定多个互斥量;谨慎使用(建议一个一个锁);
	system("pause");
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值