C++多线程之互斥量

 在学习互斥量之前,我们要清楚什么情况下需要使用互斥量,第一个例子中有一段共享数据g_v,在main()函数中创建了10个线程,这10个线程的入口函数均为myprint(),在函数中打印出共享数据,代码如下:

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

using namespace std;
vector<int> g_v = { 1,2,3 };   //共享数据
void myprint(int inum)
{
	//多个线程可以用同一个线程入口函数
	/*cout << "myprint线程开始执行,线程编号是:" << inum << endl;

	cout << "myprint线程结束了,线程编号是:" << inum << endl;*/
	cout << "id=" << this_thread::get_id() << "的线程,打印g_v值" << g_v[0] << g_v[1] << g_v[2] << endl;
	return;
}

int main()
{
	//创建和等待多个线程
	vector<thread> mythread;
	for (int i = 0; i < 10; i++)
	{
		mythread.push_back(thread(myprint, i));//创建并开始执行线程

	}
	for (auto iter = mythread.begin(); iter!=mythread.end();iter++)
	{
		iter->join();
	}
	cout << "main thread gg" << endl;

	//数据共享问题
	return 0;
}

 运行结果如下:
在这里插入图片描述
 可以看到程序正常运行,说明只读数据的共享是安全稳定的,不需要特别的处理手段
 我们再来看看有读有写的数据的情况,程序背景如下:
–>模拟一个网络游戏服务器,
   服务器有两个线程,一个线程用于收集玩家命令(范例中简化用一个数组代表),并把命令写到一个队列中。
   另一个线程用于从队列中取出玩家发出的命令,解析,执行玩家需要的动作。
代码如下:

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


using namespace std;

class A
{
public:
	//线程一,入队列
	void inMsgRecvQ()
	{
		for (int i = 0; i < 100000; i++)
		{
			cout << "接收消息的线程执行,插入一个元素  " << i << endl;
			msgRecvQ.push_back(i);//假设数字i就是玩家收到的命令
		}
	}
	
	//线程二,从消息队列中去数据
	void outMsgRecvQ()
	{
	
		for (int i = 0; i < 100000; i++)
		{
			
			if (!msgRecvQ.empty())
			{
				//表示消息队列不为空
				int command = msgRecvQ.front();//返回第一个元素,但不检查元素是否存在
				msgRecvQ.pop_front();          //移除第一个元素,不返回
			}
			else
			{
				cout << "读取消息队列执行,但目前队列中没有信息! " << i << endl;
			}
		}
		cout << "线程二执行完毕!" << endl;
	}

private:
	list<int> msgRecvQ;//容器,用于接收玩家发过来的命令
};


int main()
{
	A myTask;
	thread myOutMsgThread(&A::outMsgRecvQ, &myTask);//第二个参数是引用类型,这样才能保证线程里用的是同一个对象
	thread myInMsgThread(&A::inMsgRecvQ, &myTask);

	myInMsgThread.join();
	myOutMsgThread.join();

	return 0;
}

 执行结果如下:
在这里插入图片描述

处理程序奔溃,因为在线程二读取队列内容时线程一也在插入数据,导致了不确定性。最简单的处理是读写不能同时进行,不能同时写,不能同时读。为了保护共享数据我们引入了“互斥量mutex”,
先lock(),操作共享数据,再unlock(),lock()和unlock()必须成对使用。
 在使用mutex前,先声明mutex my_mutex;具体使用如下:

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


using namespace std;

class A
{
public:
	//线程一,入队列
	void inMsgRecvQ()
	{
		for (int i = 0; i < 100000; i++)
		{
			cout << "接收消息的线程执行,插入一个元素  " << i << endl;
			my_mutex.lock();
			msgRecvQ.push_back(i);//假设数字i就是玩家收到的命令
			my_mutex.unlock();
		}
	}
	bool outMsgDispose(int &command)
	{
		my_mutex.lock();
		if (!msgRecvQ.empty())
		{
			//表示消息队列不为空
			int command = msgRecvQ.front();//返回第一个元素,但不检查元素是否存在
			msgRecvQ.pop_front();          //移除第一个元素,不返回
			my_mutex.unlock();
			return true;
		}
		my_mutex.unlock();
		return false;
	}
	//线程二,从消息队列中去数据
	void outMsgRecvQ()
	{
		int comment = 0;
		for (int i = 0; i < 100000; i++)
		{
			bool result = outMsgDispose(comment);
			if (result==true)
			{
				cout << "线程一执行,取出一个元素" << comment << endl;
			}
			else
			{
				cout << "读取消息队列执行,但目前队列中没有信息! " << i<<endl;
			}
		}
		cout << "线程二执行完毕!" << endl;
	}

private:
	list<int> msgRecvQ;//容器,用于接收玩家发过来的命令
	mutex my_mutex;
};


int main()
{
	A myTask;
	thread myOutMsgThread(&A::outMsgRecvQ, &myTask);//第二个参数是引用类型,这样才能保证线程里用的是同一个对象
	thread myInMsgThread(&A::inMsgRecvQ, &myTask);

	myInMsgThread.join();
	myOutMsgThread.join();

	return 0;
}

 正常运行,如下:
在这里插入图片描述

 为了防止大家忘记unlock(),C++中引入了std::lock_guard的类模板。
std::lock_guard可以直接取代lock()和unlock(),也就是说你使用lock_guard()后就不能使用lock()和unlock()了。

lock_guard my_guard(my_mutex);
lock_guard构造函数里执行了mutex::lock(),析构函数里执行了mutex::unlock().
在这里插入图片描述
 程序也能正常运行。

posted @ 2019-08-07 17:03  pokeCode  阅读(486)  评论(0编辑  收藏  举报