C++11 并发与多线程(五、互斥量概念,用法)

一、互斥量的基本概念

  1. 互斥量:互斥量是个类对象,理解成一把锁(保护共享数据,其他想操作共享数据的线程必须等待解锁)
  2. 注意:互斥量使用要小心,保护数据不多也不少,少了则没达到保护效果,多了则影响效率

二、互斥量的用法

  1. 包含头文件
	#include <mutex>

1. lock()互斥锁

lock()加锁,unlock() 解锁,两者必须成对使用

	class Test
	{
	public:
		Test() {};
		~Test() {};
	
		//把收到的消息(玩家命令)入到一个队列的线程
		void InQueue()
		{
			for (int i = 0; i < 100000; ++i)
			{
				//std::this_thread::sleep_for(std::chrono::milliseconds(1000));
				cout << "插入一个元素 : " << i << endl;
				mutex.lock();
				m_list.push_back(i);
				mutex.unlock();
			}
		}
		bool OutMsg(int &nCommand)
		{
			mutex.lock();
			if (!m_list.empty())	//判断是否为空的过程也是一种读,所以也必须加锁
			{
				nCommand = m_list.front();	//返回第一个元素,但不检查元素是否存在 所以要判断是否为空
				m_list.pop_front();		//移除第一个元素,但不返回
										//考虑处理数据
				mutex.unlock();
				return true;	//注意返回之前如果加锁了,就必须要解锁
			}
			mutex.unlock();
			return false;
		}
		//出队列线程函数
		void OutQueue()
		{
			int num = 0;
			for (int i = 0; i < 100000; ++i)
			{
				bool bResult = OutMsg(num);
				if (true == bResult)
				{
					cout << "移除一个元素 : " << num << endl;
				}
				else
				{
					cout << "队列中数据为空" << endl;
				}
			}
		}
	
	private:
		list<int> m_list;	//容器(消息队列),专门用于代表玩家发过来的命令
		mutex mutex;
	};
	
	
	int main()
	{
		Test test;
		thread objIn(&Test::InQueue, &test);	//第二个参数是引用,才能保证线程里用的是同一个对象
		thread objOut(&Test::OutQueue, &test);
	
		objIn.join();
		objOut.join();
		cout << "I Love China" << endl;
	
	    return 0;
	}

2.lock_guard()锁

  • std::lock_guard类模板:可以直接取代lock()和unlock(), 使用之后就不能再使用lock()和unlock()
  • std::lock_guard 原理: 构造函数里执行了mutex::lock(),析构函数里面执行了mutex::unlock();
  • 用法
	std::lock_guard<std::mutex> guard(mutex);
	bool OutMsg(int &nCommand)
	{
		lock_guard<std::mutex> guard(mutex);
		//mutex.lock();
		if (!m_list.empty())	//判断是否为空的过程也是一种读,所以也必须加锁
		{
			nCommand = m_list.front();	//返回第一个元素,但不检查元素是否存在 所以要判断是否为空
			m_list.pop_front();		//移除第一个元素,但不返回
									//考虑处理数据
			//mutex.unlock();
			return true;	//注意返回之前如果加锁了,就必须要解锁
		}
		//mutex.unlock();
		return false;
	}

可以加 {} 作用域来使guard解锁,提前结束生命周期,调用析构函数

	{
		lock_guard<std::mutex> guard(mutex);
		m_list.push_back(i);
	}

三、死锁

1. 死锁的概念

  • 死锁的概念:是指两个或者两个以上线程在执行过程中,因争夺资源而产生互相等待的现象,若无外力作用,他们都将无法推进下去,此时,称系统处于死锁

2. 死锁的代码演示

  • 死锁代码演示:
	class Test
	{
	public:
		Test() {};
		~Test() {};
	
		//把收到的消息(玩家命令)入到一个队列的线程
		void InQueue()
		{
			for (int i = 0; i < 100000; ++i)
			{
				cout << "插入一个元素 : " << i << endl;
				mutex1.lock();
				//两个锁不一定是挨着的
				mutex2.lock();
	
				m_list.push_back(i);
	
				mutex1.unlock();
				mutex2.unlock();
			}
		}
		bool OutMsg(int &nCommand)
		{
			mutex2.lock();
			mutex1.lock();
			if (!m_list.empty())	//判断是否为空的过程也是一种读,所以也必须加锁
			{
				nCommand = m_list.front();	//返回第一个元素,但不检查元素是否存在 所以要判断是否为空
				m_list.pop_front();		//移除第一个元素,但不返回
							
				//考虑处理数据
				mutex2.unlock();
				mutex1.unlock();
				return true;	//注意返回之前如果加锁了,就必须要解锁
			}
			mutex2.unlock();
			mutex1.unlock();
			return false;
		}
		//出队列线程函数
		void OutQueue()
		{
			int num = 0;
			for (int i = 0; i < 100000; ++i)
			{
				bool bResult = OutMsg(num);
				if (true == bResult)
				{
					cout << "移除一个元素 : " << num << endl;
				}
				else
				{
					cout << "队列中数据为空" << endl;
				}
			}
		}
	
	private:
		list<int> m_list;	//容器(消息队列),专门用于代表玩家发过来的命令
		mutex mutex1;
		mutex mutex2;
	};
	
	
	int main()
	{
		Test test;
		thread objIn(&Test::InQueue, &test);	//第二个参数是引用,才能保证线程里用的是同一个对象
		thread objOut(&Test::OutQueue, &test);
	
		objIn.join();
		objOut.join();
		cout << "I Love China" << endl;
	
	    return 0;
	}

3. 死锁的解决办法

  • 死锁的解决办法:只要两个互斥量加锁顺序保持一致
  • std::lock()函数模板:用来处理多个互斥量,能一次锁住两个或两个以上的互斥量(至少两个),它不存在这种因为在多个线程中,因为锁的顺序导致死锁的风险问题;如果互斥量中有一个没锁住,它就在那里等待,等所有互斥量都锁住,它才能往下走(返回);特点:要么两个互斥量都锁住,要么两个都没锁住,如果只锁了一个,另外一个没锁成功,则他立即把已经锁住的解锁
  • 用法:
    std::lock(mutex1,mutex2);	//mutex1 和 mutex2 两者顺序无关
    
  • 代码演示:
class Test
{
public:
	Test() {};
	~Test() {};

	//把收到的消息(玩家命令)入到一个队列的线程
	void InQueue()
	{
		for (int i = 0; i < 100000; ++i)
		{
			cout << "插入一个元素 : " << i << endl;
			
			std::lock(mutex1, mutex2);
			m_list.push_back(i);

			mutex1.unlock();
			mutex2.unlock();
		}
	}
	bool OutMsg(int &nCommand)
	{
		lock(mutex1, mutex2);
		if (!m_list.empty())	//判断是否为空的过程也是一种读,所以也必须加锁
		{
			nCommand = m_list.front();	//返回第一个元素,但不检查元素是否存在 所以要判断是否为空
			m_list.pop_front();		//移除第一个元素,但不返回
						
			//考虑处理数据
			mutex2.unlock();
			mutex1.unlock();
			return true;	//注意返回之前如果加锁了,就必须要解锁
		}
		mutex2.unlock();
		mutex1.unlock();
		return false;
	}
	//出队列线程函数
	void OutQueue()
	{
		int num = 0;
		for (int i = 0; i < 100000; ++i)
		{
			bool bResult = OutMsg(num);
			if (true == bResult)
			{
				cout << "移除一个元素 : " << num << endl;
			}
			else
			{
				cout << "队列中数据为空" << endl;
			}
		}
	}

private:
	list<int> m_list;	//容器(消息队列),专门用于代表玩家发过来的命令
	mutex mutex1;
	mutex mutex2;
};


int main()
{
	Test test;
	thread objIn(&Test::InQueue, &test);	//第二个参数是引用,才能保证线程里用的是同一个对象
	thread objOut(&Test::OutQueue, &test);

	objIn.join();
	objOut.join();
	cout << "I Love China" << endl;

    return 0;
}
  • std::lock_guard()的std::adopt_lock参数 : std::adopt_lock是个结构体对象,起一个标记作用:表示这个互斥量已经lock(),不需要在std::lock_guard构造函数里面对对象进行再次lock()了
  • 代码演示:
class Test
{
public:
	Test() {};
	~Test() {};

	//把收到的消息(玩家命令)入到一个队列的线程
	void InQueue()
	{
		for (int i = 0; i < 100000; ++i)
		{
			cout << "插入一个元素 : " << i << endl;
			
			std::lock(mutex1, mutex2);
			std::lock_guard<std::mutex> guard1(mutex1, std::adopt_lock);
			std::lock_guard<std::mutex> guard2(mutex2, std::adopt_lock);

			m_list.push_back(i);
		}
	}
	bool OutMsg(int &nCommand)
	{
		lock(mutex1, mutex2);
		std::lock_guard<std::mutex> guard1(mutex1, std::adopt_lock);
		std::lock_guard<std::mutex> guard2(mutex2, std::adopt_lock);
		if (!m_list.empty())	//判断是否为空的过程也是一种读,所以也必须加锁
		{
			nCommand = m_list.front();	//返回第一个元素,但不检查元素是否存在 所以要判断是否为空
			m_list.pop_front();		//移除第一个元素,但不返回
						
			return true;	//注意返回之前如果加锁了,就必须要解锁
		}
		return false;
	}
	//出队列线程函数
	void OutQueue()
	{
		int num = 0;
		for (int i = 0; i < 100000; ++i)
		{
			bool bResult = OutMsg(num);
			if (true == bResult)
			{
				cout << "移除一个元素 : " << num << endl;
			}
			else
			{
				cout << "队列中数据为空" << endl;
			}
		}
	}

private:
	list<int> m_list;	//容器(消息队列),专门用于代表玩家发过来的命令
	mutex mutex1;
	mutex mutex2;
};


int main()
{
	Test test;
	thread objIn(&Test::InQueue, &test);	//第二个参数是引用,才能保证线程里用的是同一个对象
	thread objOut(&Test::OutQueue, &test);

	objIn.join();
	objOut.join();
	cout << "I Love China" << endl;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值