C++多线程学习笔记04(多线程创建、等待、数据共享,互锁)

一.创建和等待多个线程
总结:(1)执行顺序是乱的,跟操作系统内部对线程的运行调度机制有关
(2)主线程等待子线程结束,最后主线程结束
(3)将thread对象放入容器中,对创建大量的线程管理方便

void myprint(int inum)
{
	cout << "myprint线程开始执行,编号 = "<<inum << endl;
	cout << "myprint线程结束执行,编号 = " << inum << endl;
}
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 << "主线程结束" << endl;
	return 0;
}

myprint线程开始执行,编号 = 0myprint线程开始执行,编号 = 2
myprint线程开始执行,编号 = 4myprint线程开始执行,编号 = 1

myprint线程结束执行,编号 = 1
myprint线程开始执行,编号 = 8myprint线程开始执行,编号 = 9
myprint线程结束执行,编号 = 9
myprint线程开始执行,编号 = 6
myprint线程结束执行,编号 = 6

myprint线程结束执行,编号 = 4
myprint线程开始执行,编号 = 7
myprint线程开始执行,编号 = 3
myprint线程结束执行,编号 = 3

myprint线程结束执行,编号 = 8
myprint线程结束执行,编号 = 2
myprint线程结束执行,编号 = 7
myprint线程结束执行,编号 = 0
myprint线程开始执行,编号 = 5
myprint线程结束执行,编号 = 5
主线程结束
请按任意键继续. . .

二.数据共享分析
(1)只读数据是安全稳定的,不需要特别的什么处理手段,直接读就可以
(2)有读有写
//最简单处理,读的时候不能写,写的时候不能读。两个线程不能同时写或者读。
(3)数据共享

三.案例
假如网络服务器有两个线程:一个线程收集玩家命令(数字代表玩家发的命令),并把数据命令写到队列中。另一个线程,从队列中取出玩家发送来的命令,解析,然后执行玩家需要的动作。
知识点:list:频繁的按顺序插入和删除数据处理效率高 ;vector 随机插入和删除数据效率高;
保护共享数据:操作时,某个线程 用代码把共享数据锁住、然后操作数据、最后解锁,其他想操作共享数据的线程必须等待,锁住、操作、解锁。
1.互斥量(mutex)的基本概念
互斥量是类对象, 理解成一把锁多个线程尝试用lock()成员函数加锁 ,其中只有一个线程锁定成功。如果没锁成功,流程卡在lock() 不断尝试去锁。
注意点:使用时保护的数据不能太多。
2.使用 先lock() 操作共享数据 ,然后 unlock()解锁。
****使用规则:成对使用 。

3.引入std::lock_guard的类模板
std::lock_guard是类模板,可直接取代lock()和unlock(). {}包裹内执行可以限制范围

4.死锁
至少有两把锁(两个互斥量才会产生) 例如: 金锁(JinLock) 银锁(YinLock)
有两个线程,线程A执行,先锁金锁,lock()成功,然后 锁 lock银锁 。。。
出现上下文切换
线程B执行, 先锁 银锁 因为银锁还没被锁,所以银锁lock()成功 ,线程B 又要去lock 金锁 。。。
此刻,死锁产生;
线程A因为拿不到银锁,流程走不下去
线程B因为拿不到金锁,流程走不下去
最后都在等待,流程无法执行。

解决方法:只要保证上锁的顺序一致可解决。

  1. std::lock()函数模板 ,用来处理多个互斥量。
    能力:一次锁住两个或两个以上的互斥量
    不存在多个线程中,因为锁的顺序问题导致死锁风险;
    特点:要么两个都锁住,要么都没锁。如果只锁住一个,另一个没锁柱,则立即解锁已经锁住的解锁;

6.std::lock_guard的std::adopt_lock参数与std::lock()搭配
std::lock_guard 是一个结构体对象,起一个标记作用:作用就是表示这个互斥量已经lock();
不需要在std::lock_guardstd::mutex里面对对象进行再次lock()

一个锁

// 线程3.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include<thread>
#include<iostream>
using namespace std;
#include<vector>
#include<list>
#include<mutex>
class A {
public:
	//把收到的消息(玩家命令)写入到一个队列线程
	void inMsgRecvQueue()
	{
		for (int i =0;i<10000;i++)
		{
			cout << "inMesRecvQueue()执行,插入元素" << i << endl;
			my_mutex1.lock();
			//.......有数据处理,不一定相近,各有保护的数据,
			msgRecvQueue.push_back(i); //假设数字为接受的命令,直接进入消息队列  
			my_mutex1.unlock();
		}
	}
	bool outMsgLULProc(int &command)
	{
		my_mutex1.lock();  
		if (!msgRecvQueue.empty())
		{
			//消息不为空
			command = msgRecvQueue.front();//返回第一个元素,但不检查元素是否存在
			msgRecvQueue.pop_front();//移除第一个元素,但不返回
			 //这里是处理数据....
			//cout << "处理数据" << command << endl;
			my_mutex1.unlock();
			return true;
		}
		my_mutex1.unlock();
		return false;
	}

	//把数据从消息队列中取出的线程
	void outMsgRecvQueue()
	{
		int command = 0;
		for (int i = 0; i<10000; i++)
		{
			bool result = outMsgLULProc(command);
			if(result)
				cout << "outMsgRecvQueue()函数处理数据,去除一个元素 =" << command << endl;
			else
			{
				//消息队列为空
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" <<i<< endl;
			}
		}
		cout << "消息队列处理完成" << endl;
	}
private:
	list<int> msgRecvQueue;//容器,专门用于代表玩家发送过来的消息
	mutex my_mutex1;//创建互斥量
};

int main()
{
	A myobj;
	thread myOutMsgObj(&A::outMsgRecvQueue,&myobj);//第二个参数是引用  避免复制
	thread myInMsgObj(&A::inMsgRecvQueue,&myobj);
	myOutMsgObj.join();
	myInMsgObj.join();
	cout << "主线程结束" << endl;
	return 0;
}

两个锁

// 线程3.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include<thread>
#include<iostream>
using namespace std;
#include<vector>
#include<list>
#include<mutex>

class A {
public:
	//把收到的消息(玩家命令)写入到一个队列线程
	void inMsgRecvQueue()
	{
		for (int i =0;i<10000;i++)
		{
			cout << "inMesRecvQueue()执行,插入元素" << i << endl;
			my_mutex1.lock();
			//.......有数据处理,不一定相近,各有保护的数据,
			my_mutex2.lock();
			msgRecvQueue.push_back(i); //假设数字为接受的命令,直接进入消息队列  
			my_mutex2.unlock();
			my_mutex1.unlock();
			}
		}
	}

	bool outMsgLULProc(int &command)
	{
		my_mutex1.lock();  
		my_mutex2.lock();
		if (!msgRecvQueue.empty())
		{
			//消息不为空
			command = msgRecvQueue.front();//返回第一个元素,但不检查元素是否存在
			msgRecvQueue.pop_front();//移除第一个元素,但不返回
			//这里是处理数据....
			//cout << "处理数据" << command << endl;
			my_mutex2.unlock();
			my_mutex1.unlock();
			return true;
		}
		my_mutex2.unlock();
		my_mutex1.unlock();
		return false;
	}
	//把数据从消息队列中取出的线程
	void outMsgRecvQueue()
	{
		int command = 0;
		for (int i = 0; i<10000; i++)
		{

			bool result = outMsgLULProc(command);
			if(result)
				cout << "outMsgRecvQueue()函数处理数据,去除一个元素 =" << command << endl;
			else
			{
				//消息队列为空
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" <<i<< endl;
			}
		}
		cout << "消息队列处理完成" << endl;
	}
private:
	list<int> msgRecvQueue;//容器,专门用于代表玩家发送过来的消息
	mutex my_mutex1;//创建互斥量
	mutex my_mutex2;//创建互斥量
};
int main()
{
	A myobj;
	thread myOutMsgObj(&A::outMsgRecvQueue,&myobj);//第二个参数是引用  避免复制
	thread myInMsgObj(&A::inMsgRecvQueue,&myobj);
	myOutMsgObj.join();
	myInMsgObj.join();
	cout << "主线程结束" << endl;
	return 0;

}

std::lock_guard模板

#include "stdafx.h"
#include<thread>
#include<iostream>
using namespace std;
#include<vector>
#include<list>
#include<mutex>

class A {
public:
	//把收到的消息(玩家命令)写入到一个队列线程
	void inMsgRecvQueue()
	{
		for (int i =0;i<10000;i++)
		{
			cout << "inMesRecvQueue()执行,插入元素" << i << endl;
			std::lock_guard<std::mutex> sbguard1(my_mutex1);
			msgRecvQueue.push_back(i); //假设数字为接受的命令,直接进入消息队列  
			}
		}
	}


	bool outMsgLULProc(int &command)
	{
		std::lock_guard<std::mutex> sbguard1(my_mutex1);
		if (!msgRecvQueue.empty())
		{
			//消息不为空
			command = msgRecvQueue.front();//返回第一个元素,但不检查元素是否存在
			msgRecvQueue.pop_front();//移除第一个元素,但不返回
			//这里是处理数据....
			//cout << "处理数据" << command << endl;
			return true;
		}
		return false;
	}

	//把数据从消息队列中取出的线程
	void outMsgRecvQueue()
	{
		int command = 0;
		for (int i = 0; i<10000; i++)
		{
			bool result = outMsgLULProc(command);
			if(result)
				cout << "outMsgRecvQueue()函数处理数据,去除一个元素 =" << command << endl;
			else
			{
				//消息队列为空
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" <<i<< endl;
			}
		}
		cout << "消息队列处理完成" << endl;
	}
private:
	list<int> msgRecvQueue;//容器,专门用于代表玩家发送过来的消息
	mutex my_mutex1;//创建互斥量
	mutex my_mutex2;//创建互斥量
};

int main()
{
	A myobj;
	thread myOutMsgObj(&A::outMsgRecvQueue,&myobj);//第二个参数是引用  避免复制
	thread myInMsgObj(&A::inMsgRecvQueue,&myobj);
	myOutMsgObj.join();
	myInMsgObj.join();
	cout << "主线程结束" << endl;
	return 0;

}

std::lock()函数模板

// 线程3.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include<thread>
#include<iostream>
using namespace std;
#include<vector>
#include<list>
#include<mutex>
class A {
public:
	//把收到的消息(玩家命令)写入到一个队列线程
	void inMsgRecvQueue()
	{
		for (int i =0;i<10000;i++)
		{
			cout << "inMesRecvQueue()执行,插入元素" << i << endl;
			{
				std::lock(my_mutex1, my_mutex2);
				msgRecvQueue.push_back(i); //假设数字为接受的命令,直接进入消息队列  
				my_mutex2.unlock();
				my_mutex1.unlock();
			}
		}
	}


	bool outMsgLULProc(int &command)
	{
		std::lock(my_mutex1, my_mutex2); //相当于每个互斥量都调用.lock()		
		if (!msgRecvQueue.empty())
		{
			//消息不为空
			command = msgRecvQueue.front();//返回第一个元素,但不检查元素是否存在

			msgRecvQueue.pop_front();//移除第一个元素,但不返回
									 //这里是处理数据....
			//cout << "处理数据" << command << endl;
			my_mutex2.unlock();
			my_mutex1.unlock();
			return true;
		}
		my_mutex2.unlock();
		my_mutex1.unlock();
		return false;
	}

	//把数据从消息队列中取出的线程
	void outMsgRecvQueue()
	{
		int command = 0;
		for (int i = 0; i<10000; i++)
		{

			bool result = outMsgLULProc(command);
			if(result)
				cout << "outMsgRecvQueue()函数处理数据,去除一个元素 =" << command << endl;
			else
			{
				//消息队列为空
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" <<i<< endl;
			}
		}
		cout << "消息队列处理完成" << endl;
	}
private:
	list<int> msgRecvQueue;//容器,专门用于代表玩家发送过来的消息
	mutex my_mutex1;//创建互斥量
	mutex my_mutex2;//创建互斥量
};

int main()
{
	A myobj;
	thread myOutMsgObj(&A::outMsgRecvQueue,&myobj);//第二个参数是引用  避免复制
	thread myInMsgObj(&A::inMsgRecvQueue,&myobj);
	myOutMsgObj.join();
	myInMsgObj.join();
	cout << "主线程结束" << endl;
	return 0;

}

std::lock_guard的std::adopt_lock参数 与std::lock()搭配

// 线程3.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include<thread>
#include<iostream>
using namespace std;
#include<vector>
#include<list>
#include<mutex>
class A {
public:
	//把收到的消息(玩家命令)写入到一个队列线程
	void inMsgRecvQueue()
	{
		for (int i =0;i<10000;i++)
		{
			cout << "inMesRecvQueue()执行,插入元素" << i << endl;
			{
				std::lock(my_mutex1, my_mutex2);
				std::lock_guard<std::mutex> sbguard1(my_mutex1,std::adopt_lock);  //{}包裹内执行  限制范围
				std::lock_guard<std::mutex> sbguard2(my_mutex2,std::adopt_lock);
				
				msgRecvQueue.push_back(i); //假设数字为接受的命令,直接进入消息队列  
			}
		}
	}


	bool outMsgLULProc(int &command)
	{
		std::lock(my_mutex1, my_mutex2); //相当于每个互斥量都调用.lock()
		std::lock_guard<std::mutex> sbguard1(my_mutex1,std::adopt_lock); //sbguard是随便起的对象名  lock_guard构造函数执行了mutex::lock(); 析构函数执行mutex::unlock();
		std::lock_guard<std::mutex> sbguard2(my_mutex2,std::adopt_lock);
		if (!msgRecvQueue.empty())
		{
			//消息不为空
			command = msgRecvQueue.front();//返回第一个元素,但不检查元素是否存在
			msgRecvQueue.pop_front();//移除第一个元素,但不返回
			//这里是处理数据....
			//cout << "处理数据" << command << endl;
			return true;
		}
		return false;
	}

	//把数据从消息队列中取出的线程
	void outMsgRecvQueue()
	{
		int command = 0;
		for (int i = 0; i<10000; i++)
		{

			bool result = outMsgLULProc(command);
			if(result)
				cout << "outMsgRecvQueue()函数处理数据,去除一个元素 =" << command << endl;
			else
			{
				//消息队列为空
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" <<i<< endl;
			}
		}
		cout << "消息队列处理完成" << endl;
	}
private:
	list<int> msgRecvQueue;//容器,专门用于代表玩家发送过来的消息
	mutex my_mutex1;//创建互斥量
	mutex my_mutex2;//创建互斥量
};

int main()
{
	A myobj;
	thread myOutMsgObj(&A::outMsgRecvQueue,&myobj);//第二个参数是引用  避免复制
	thread myInMsgObj(&A::inMsgRecvQueue,&myobj);
	myOutMsgObj.join();
	myInMsgObj.join();
	cout << "主线程结束" << endl;
	return 0;

}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值