一.创建和等待多个线程
总结:(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因为拿不到金锁,流程走不下去
最后都在等待,流程无法执行。
解决方法:只要保证上锁的顺序一致可解决。
- 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;
}