编程语言
首页 > 编程语言> > C++ 并发与多线程(五)

C++ 并发与多线程(五)

作者:互联网

unique_lock详解

1.unique_lock取代lock_guard

// 并发与多线程2_4.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

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


//vector<int>g_v = { 1,2,3 };
//
//void Myprint(int inum)
//{
//	cout << "myprint线程开始执行了,线程编号" << inum << endl;
//	cout << "myprint线程结束执行了,线程编号" << inum << endl;
//	cout << "id 为" << std::this_thread::get_id() << "打印g_v值" << g_v[0] << g_v[1] << g_v[2] << endl;
//
//}



class A
{
public:
	//把玩家命令放入到一个队列的进程
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			//my_mutex.lock();
			cout << "inMsgRecvQueue执行,插入一个元素" << i << endl;
			{


				//my_mutex1.lock();		//先锁金锁 实际中两个lock间会执行其他的东西
				//my_mutex2.lock();		//再锁银锁
				//使用std::lock()
				//std::lock(my_mutex1, my_mutex2);    //相当与每个互斥量都调用了lock
				//std::lock_guard<mutex>sbguard1(my_mutex1, adopt_lock);
				//std::lock_guard<mutex>sbguard2(my_mutex2, adopt_lock);
				std::unique_lock<std::mutex>sbguard1(my_mutex1);
				msgRecvQueue.push_back(i);     //假设数字i为命令 放入队列	

				//my_mutex2.unlock();				//顺序无所谓
				//my_mutex1.unlock();

			}
		}
	}


	bool outMsgLULproc(int &command)
	{
		//lock_guard<mutex>sbguard(my_mutex1);       //sbguard是对象名
		//my_mutex1.lock();
		//my_mutex2.lock();
		//std::lock(my_mutex1, my_mutex2);
		std::unique_lock<std::mutex>sbguard1(my_mutex1);
		if (!msgRecvQueue.empty())
		{
		    command = msgRecvQueue.front();      //返回第一个元素但不检查元素是否存在
			msgRecvQueue.pop_front();            //移除第一个元素但不返回
			//处理数据。。。。。
			/*my_mutex1.unlock();
			my_mutex2.unlock();*/
			return true;
		}	
		else
		{
			//my_mutex1.unlock();
			//my_mutex2.unlock();
			return false; 
		}
	
	}

	//读取命令的线程
	void outMsgRecvQueue()
	{
		int command = 0;	
		for (int i = 0; i < 100000; i++)
		{
			bool result = outMsgLULproc(command);
			if (result == true)
			{
			
				cout << "outMsgRecvQueue 执行,取出一个元素" << command << endl;
				//数据处理
			}

		
		}
		cout << "end" << endl;
	
	}
private:

	std::list<int>msgRecvQueue;
	mutex my_mutex1;           //创建一个互斥量
	mutex my_mutex2;     

};

int main()
{
 //   //一 创建线程和等待多个线程
	//vector<thread>mythreads;
	创建10个线程,线程入口函数统一使用 myprint
	1)多线程执行顺序是乱的
	2)这种join写法更容易写出稳定程序
	3)把thread对象放入到容器里,对管理大量线程有帮助
	//for (int i = 0; i < 10; i++)
	//{
	//	//创建10个线程,已经开始执行
	//	mythreads.push_back(thread(Myprint, i));
	//
	//}
	//for (auto iter = mythreads.begin(); iter != mythreads.end(); ++iter)
	//{
	//	iter->join();
	//}
	//cout << "I LOVE CHINA" << endl;

	//二 数据共享
	//2.1只读数据
	//2.2 有读有写
	//最简单的不崩溃处理 读和写不能同时进行
	//2.3其他案例
	//数据共享:    
	
	//三 共享数据的保护案例代码
	//网络游戏服务器,有两个自己创建的线程,一个线程手机玩家命令(数字表示),并把名利数据写入到一个队列中。
	//另一个线程从队列中取出玩家发送来的命令,解析,然后执行玩家的动作。
	//使用list,频繁的按顺序插入和删除时效率较高


	//用成员函数作为线程函数的方法写线程

	A myobja;
	thread myOutnMsg(&A::outMsgRecvQueue, &myobja);
	thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
	myOutnMsg.join();
	myInMsgObj.join();

	//四 互斥量的概念
	//步骤:先lock 操作共享数据 然后unlock
	//lock和unlock要成对使用。有lock忘记unlock的问题非常难排查
	//为了防止忘记unlock(),引入了一个叫std::lock_guard的类模板
	//智能指针(unique_ptr<>)

	//std::lock_guard类模板 直接取代lock()和unlock(),用了类模板不能再用lock和unlock
	//要将保护量放在lock和unlock里


	//五 死锁
	/*
	**死锁问题的前提条件是:有至少两个锁,即至少两个互斥量,金锁(Jinlock),银锁(Yinlock)**
	两个线程A,B
	线程A执行时,先锁金锁,然后去锁银锁
	两个线程出现了上下文切换,线程B执行了,线程B先锁银锁,因为银锁没有被A锁上,所以被B锁上了,然后线程B去锁金锁
	此时产生了死锁
	线程A锁不了银锁,流程走不下去
	线程B锁不了金锁,流程走不下去
	死锁产生的关键是两个互斥量的上锁顺序不一致
	*/

	//5.1死锁演示
	//5.2死锁的一般解决方案
	//只要保证两个互斥量上锁的顺序一致,就不会造成死锁。


	//5.3 std::lock()函数模板
	//能力:一次锁住两个或者两个以上的互斥量(至少两个,多了不限,一个不行)
	//不存在因为锁头的顺序问题导致出现死锁问题。
	//如果互斥量中有一个没锁住,则等待,等待所有互斥量否锁住,才能继续往下走
	//特点:要么两个互斥量都锁住,要么两个互斥量都没锁住。如果只锁了一个,另外一个没成功,则立即解锁已经锁住的。
	//用来处理多个互斥量的情况

	//5.4 std:lock_guard的std:adopt_lock参数


	//六 unique_lock取代lock_guard	//unique_lock是个类模板,工作中推荐使用lock_guard
	//lock_guard取代了mutex的lock和unlock
	//unique_lock比lock_guard灵活,效率略差,内存占用稍多。


}


在这里插入图片描述

2.unique_lock的第二个参数

2.1 adopt_lock

// 并发与多线程2_4.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

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


//vector<int>g_v = { 1,2,3 };
//
//void Myprint(int inum)
//{
//	cout << "myprint线程开始执行了,线程编号" << inum << endl;
//	cout << "myprint线程结束执行了,线程编号" << inum << endl;
//	cout << "id 为" << std::this_thread::get_id() << "打印g_v值" << g_v[0] << g_v[1] << g_v[2] << endl;
//
//}



class A
{
public:
	//把玩家命令放入到一个队列的进程
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			//my_mutex.lock();
			cout << "inMsgRecvQueue执行,插入一个元素" << i << endl;
			{


				//my_mutex1.lock();		//先锁金锁 实际中两个lock间会执行其他的东西
				//my_mutex2.lock();		//再锁银锁
				//使用std::lock()
				//std::lock(my_mutex1, my_mutex2);    //相当与每个互斥量都调用了lock
				//std::lock_guard<mutex>sbguard1(my_mutex1, adopt_lock);
				//std::lock_guard<mutex>sbguard2(my_mutex2, adopt_lock);
				//std::unique_lock<std::mutex>sbguard1(my_mutex1);

				my_mutex1.lock();                     //in线程后到
				std::unique_lock<std::mutex>sbguard1(my_mutex1,adopt_lock);  //使用了adopt_lock 需要提前lock
				msgRecvQueue.push_back(i);     //假设数字i为命令 放入队列	

				//my_mutex2.unlock();				//顺序无所谓
				//my_mutex1.unlock();

			}
		}
	}


	bool outMsgLULproc(int &command)
	{
		//lock_guard<mutex>sbguard(my_mutex1);       //sbguard是对象名
		//my_mutex1.lock();
		//my_mutex2.lock();
		//std::lock(my_mutex1, my_mutex2);

		std::chrono::milliseconds dura(20000);    //等待20s
		std::this_thread::sleep_for(dura);		  //休息一定时长  outMsgLULproc先跑起来


		std::unique_lock<std::mutex>sbguard1(my_mutex1);
		if (!msgRecvQueue.empty())
		{
		    command = msgRecvQueue.front();      //返回第一个元素但不检查元素是否存在
			msgRecvQueue.pop_front();            //移除第一个元素但不返回
			//处理数据。。。。。
			/*my_mutex1.unlock();
			my_mutex2.unlock();*/
			return true;
		}	
		else
		{
			//my_mutex1.unlock();
			//my_mutex2.unlock();
			return false; 
		}
	
	}

	//读取命令的线程
	void outMsgRecvQueue()
	{
		int command = 0;	
		for (int i = 0; i < 100000; i++)
		{
			bool result = outMsgLULproc(command);
			if (result == true)
			{
			
				cout << "outMsgRecvQueue 执行,取出一个元素" << command << endl;
				//数据处理
			}

		
		}
		cout << "end" << endl;
	
	}
private:

	std::list<int>msgRecvQueue;
	mutex my_mutex1;           //创建一个互斥量
	mutex my_mutex2;     

};

int main()
{
 //   //一 创建线程和等待多个线程
	//vector<thread>mythreads;
	创建10个线程,线程入口函数统一使用 myprint
	1)多线程执行顺序是乱的
	2)这种join写法更容易写出稳定程序
	3)把thread对象放入到容器里,对管理大量线程有帮助
	//for (int i = 0; i < 10; i++)
	//{
	//	//创建10个线程,已经开始执行
	//	mythreads.push_back(thread(Myprint, i));
	//
	//}
	//for (auto iter = mythreads.begin(); iter != mythreads.end(); ++iter)
	//{
	//	iter->join();
	//}
	//cout << "I LOVE CHINA" << endl;

	//二 数据共享
	//2.1只读数据
	//2.2 有读有写
	//最简单的不崩溃处理 读和写不能同时进行
	//2.3其他案例
	//数据共享:    
	
	//三 共享数据的保护案例代码
	//网络游戏服务器,有两个自己创建的线程,一个线程手机玩家命令(数字表示),并把名利数据写入到一个队列中。
	//另一个线程从队列中取出玩家发送来的命令,解析,然后执行玩家的动作。
	//使用list,频繁的按顺序插入和删除时效率较高


	//用成员函数作为线程函数的方法写线程

	A myobja;
	thread myOutnMsg(&A::outMsgRecvQueue, &myobja);
	thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
	myOutnMsg.join();
	myInMsgObj.join();

	//四 互斥量的概念
	//步骤:先lock 操作共享数据 然后unlock
	//lock和unlock要成对使用。有lock忘记unlock的问题非常难排查
	//为了防止忘记unlock(),引入了一个叫std::lock_guard的类模板
	//智能指针(unique_ptr<>)

	//std::lock_guard类模板 直接取代lock()和unlock(),用了类模板不能再用lock和unlock
	//要将保护量放在lock和unlock里


	//五 死锁
	/*
	**死锁问题的前提条件是:有至少两个锁,即至少两个互斥量,金锁(Jinlock),银锁(Yinlock)**
	两个线程A,B
	线程A执行时,先锁金锁,然后去锁银锁
	两个线程出现了上下文切换,线程B执行了,线程B先锁银锁,因为银锁没有被A锁上,所以被B锁上了,然后线程B去锁金锁
	此时产生了死锁
	线程A锁不了银锁,流程走不下去
	线程B锁不了金锁,流程走不下去
	死锁产生的关键是两个互斥量的上锁顺序不一致
	*/

	//5.1死锁演示
	//5.2死锁的一般解决方案
	//只要保证两个互斥量上锁的顺序一致,就不会造成死锁。


	//5.3 std::lock()函数模板
	//能力:一次锁住两个或者两个以上的互斥量(至少两个,多了不限,一个不行)
	//不存在因为锁头的顺序问题导致出现死锁问题。
	//如果互斥量中有一个没锁住,则等待,等待所有互斥量否锁住,才能继续往下走
	//特点:要么两个互斥量都锁住,要么两个互斥量都没锁住。如果只锁了一个,另外一个没成功,则立即解锁已经锁住的。
	//用来处理多个互斥量的情况

	//5.4 std:lock_guard的std:adopt_lock参数


	//六 unique_lock取代lock_guard
	//unique_lock是个类模板,工作中推荐使用lock_guard
	//lock_guard取代了mutex的lock和unlock
	//unique_lock比lock_guard灵活,效率略差,内存占用稍多。

	//七 unique_lock的第二个参数
	//lock_guard中的adopt_lock起标记作用 表示互斥量已经被lock了,在写这个标记前需要把互斥量lock 否则报错
	//adopt的效果是:假设调用方 线程已经拥有了互斥的所有权(已经lock成功了)
	//unique_lock也有adopt_lock标记,含义相同。
	//灵活性:
	//对于同一个锁 如果其中一个线程上了锁,则另一个也需要这个锁的线程就会处于等待状态,知道前一个线程解锁,会浪费很多时间
	//使用
}

2.2 try_to_lock

尝试用mutex的lock()去锁定lock,但如果没有锁定成功,也会立即返回,并不会阻塞,用try_to_ lock的前提是不可以提前上锁,否则异常。

// 并发与多线程2_4.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

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


//vector<int>g_v = { 1,2,3 };
//
//void Myprint(int inum)
//{
//	cout << "myprint线程开始执行了,线程编号" << inum << endl;
//	cout << "myprint线程结束执行了,线程编号" << inum << endl;
//	cout << "id 为" << std::this_thread::get_id() << "打印g_v值" << g_v[0] << g_v[1] << g_v[2] << endl;
//
//}



class A
{
public:
	//把玩家命令放入到一个队列的进程
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			//my_mutex.lock();
			
			{


				//my_mutex1.lock();		//先锁金锁 实际中两个lock间会执行其他的东西
				//my_mutex2.lock();		//再锁银锁
				//使用std::lock()
				//std::lock(my_mutex1, my_mutex2);    //相当与每个互斥量都调用了lock
				//std::lock_guard<mutex>sbguard1(my_mutex1, adopt_lock);
				//std::lock_guard<mutex>sbguard2(my_mutex2, adopt_lock);
				//std::unique_lock<std::mutex>sbguard1(my_mutex1);

				//my_mutex1.lock();                     //in线程后到
				//std::unique_lock<std::mutex>sbguard1(my_mutex1,adopt_lock);  //使用了adopt_lock 需要提前lock

				//尝试加锁
				std::unique_lock<std::mutex>sbguard1(my_mutex1, std::try_to_lock);         //使用此参数的条件时互斥量没有上锁

				if (sbguard1.owns_lock())
				{
					cout << "inMsgRecvQueue执行,插入一个元素" << i << endl;
					msgRecvQueue.push_back(i);     //假设数字i为命令 放入队列
				}
				else
				{
					cout << "inMsgRecvQueue 执行,但没有上锁。。。。" << endl;
				}

					

				//my_mutex2.unlock();				//顺序无所谓
				//my_mutex1.unlock();

			}
		}
	}


	bool outMsgLULproc(int &command)
	{
		//lock_guard<mutex>sbguard(my_mutex1);       //sbguard是对象名
		//my_mutex1.lock();
		//my_mutex2.lock();
		//std::lock(my_mutex1, my_mutex2);

	

		std::unique_lock<std::mutex>sbguard1(my_mutex1);
		std::chrono::milliseconds dura(10);    //等待10ms
		std::this_thread::sleep_for(dura);		  //休息一定时长  outMsgLULproc先跑起来
		if (!msgRecvQueue.empty())
		{
		    command = msgRecvQueue.front();      //返回第一个元素但不检查元素是否存在
			msgRecvQueue.pop_front();            //移除第一个元素但不返回
			//处理数据。。。。。
			/*my_mutex1.unlock();
			my_mutex2.unlock();*/
			return true;
		}	
		else
		{
			//my_mutex1.unlock();
			//my_mutex2.unlock();
			return false; 
		}
	
	}

	//读取命令的线程
	void outMsgRecvQueue()
	{
		int command = 0;	
		for (int i = 0; i < 100000; i++)
		{
			bool result = outMsgLULproc(command);
			if (result == true)
			{
			
				cout << "outMsgRecvQueue 执行,取出一个元素" << command << endl;
				//数据处理
			}

		
		}
		cout << "end" << endl;
	
	}
private:

	std::list<int>msgRecvQueue;
	mutex my_mutex1;           //创建一个互斥量
	mutex my_mutex2;     

};

int main()
{
 //   //一 创建线程和等待多个线程
	//vector<thread>mythreads;
	创建10个线程,线程入口函数统一使用 myprint
	1)多线程执行顺序是乱的
	2)这种join写法更容易写出稳定程序
	3)把thread对象放入到容器里,对管理大量线程有帮助
	//for (int i = 0; i < 10; i++)
	//{
	//	//创建10个线程,已经开始执行
	//	mythreads.push_back(thread(Myprint, i));
	//
	//}
	//for (auto iter = mythreads.begin(); iter != mythreads.end(); ++iter)
	//{
	//	iter->join();
	//}
	//cout << "I LOVE CHINA" << endl;

	//二 数据共享
	//2.1只读数据
	//2.2 有读有写
	//最简单的不崩溃处理 读和写不能同时进行
	//2.3其他案例
	//数据共享:    
	
	//三 共享数据的保护案例代码
	//网络游戏服务器,有两个自己创建的线程,一个线程手机玩家命令(数字表示),并把名利数据写入到一个队列中。
	//另一个线程从队列中取出玩家发送来的命令,解析,然后执行玩家的动作。
	//使用list,频繁的按顺序插入和删除时效率较高


	//用成员函数作为线程函数的方法写线程

	A myobja;
	thread myOutnMsg(&A::outMsgRecvQueue, &myobja);
	thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
	myOutnMsg.join();
	myInMsgObj.join();

	//四 互斥量的概念
	//步骤:先lock 操作共享数据 然后unlock
	//lock和unlock要成对使用。有lock忘记unlock的问题非常难排查
	//为了防止忘记unlock(),引入了一个叫std::lock_guard的类模板
	//智能指针(unique_ptr<>)

	//std::lock_guard类模板 直接取代lock()和unlock(),用了类模板不能再用lock和unlock
	//要将保护量放在lock和unlock里


	//五 死锁
	/*
	**死锁问题的前提条件是:有至少两个锁,即至少两个互斥量,金锁(Jinlock),银锁(Yinlock)**
	两个线程A,B
	线程A执行时,先锁金锁,然后去锁银锁
	两个线程出现了上下文切换,线程B执行了,线程B先锁银锁,因为银锁没有被A锁上,所以被B锁上了,然后线程B去锁金锁
	此时产生了死锁
	线程A锁不了银锁,流程走不下去
	线程B锁不了金锁,流程走不下去
	死锁产生的关键是两个互斥量的上锁顺序不一致
	*/

	//5.1死锁演示
	//5.2死锁的一般解决方案
	//只要保证两个互斥量上锁的顺序一致,就不会造成死锁。


	//5.3 std::lock()函数模板
	//能力:一次锁住两个或者两个以上的互斥量(至少两个,多了不限,一个不行)
	//不存在因为锁头的顺序问题导致出现死锁问题。
	//如果互斥量中有一个没锁住,则等待,等待所有互斥量否锁住,才能继续往下走
	//特点:要么两个互斥量都锁住,要么两个互斥量都没锁住。如果只锁了一个,另外一个没成功,则立即解锁已经锁住的。
	//用来处理多个互斥量的情况

	//5.4 std:lock_guard的std:adopt_lock参数


	//六 unique_lock取代lock_guard
	//unique_lock是个类模板,工作中推荐使用lock_guard
	//lock_guard取代了mutex的lock和unlock
	//unique_lock比lock_guard灵活,效率略差,内存占用稍多。

	//七 unique_lock的第二个参数
	// 7.1 adopt_lock
	//lock_guard中的adopt_lock起标记作用 表示互斥量已经被lock了,在写这个标记前需要把互斥量lock 否则报错
	//adopt的效果是:假设调用方 线程已经拥有了互斥的所有权(已经lock成功了)
	//unique_lock也有adopt_lock标记,含义相同。
	//灵活性:
	//对于同一个锁 如果其中一个线程上了锁,则另一个也需要这个锁的线程就会处于等待状态,知道前一个线程解锁,会浪费很多时间
	

	//7.2 std::try to lock
	//尝试用mutex的lock()去锁定lock,但如果没有锁定成功,也会立即返回,并不会阻塞
	//用try to lock的前提是不可以手动上锁,否则异常


}


当没有上锁成功时,try to lock可以通过判断条件进入自定义事件
在这里插入图片描述

2.3 std::defer_lock

初始化一个没有上锁的互斥量mutex

3.unique_lock的成员函数

3.1 lock() 加锁

通过调用unique_lock的成员函数lock,自动上锁和解锁,配合defer_lock使用。

// 并发与多线程2_4.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

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


//vector<int>g_v = { 1,2,3 };
//
//void Myprint(int inum)
//{
//	cout << "myprint线程开始执行了,线程编号" << inum << endl;
//	cout << "myprint线程结束执行了,线程编号" << inum << endl;
//	cout << "id 为" << std::this_thread::get_id() << "打印g_v值" << g_v[0] << g_v[1] << g_v[2] << endl;
//
//}



class A
{
public:
	//把玩家命令放入到一个队列的进程
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			//my_mutex.lock();
			
			{


				//my_mutex1.lock();		//先锁金锁 实际中两个lock间会执行其他的东西
				//my_mutex2.lock();		//再锁银锁
				//使用std::lock()
				//std::lock(my_mutex1, my_mutex2);    //相当与每个互斥量都调用了lock
				//std::lock_guard<mutex>sbguard1(my_mutex1, adopt_lock);
				//std::lock_guard<mutex>sbguard2(my_mutex2, adopt_lock);
				//std::unique_lock<std::mutex>sbguard1(my_mutex1);

				//my_mutex1.lock();                     //in线程后到
				//std::unique_lock<std::mutex>sbguard1(my_mutex1,adopt_lock);  //使用了adopt_lock 需要提前lock

				//尝试加锁
				//std::unique_lock<std::mutex>sbguard1(my_mutex1, std::try_to_lock);         //使用此参数的条件时互斥量没有上锁

				//if (sbguard1.owns_lock())              //如果尝试上锁成功
				//{
					cout << "inMsgRecvQueue执行,插入一个元素" << i << endl;

					//创建没有加锁的my_mutex1
					std::unique_lock<std::mutex>sbguard1(my_mutex1, std::defer_lock);

					sbguard1.lock();              //不需要手动解锁

					msgRecvQueue.push_back(i);     //假设数字i为命令 放入队列

				//}
				//else								  // 如果上锁失败
				//{
				//	cout << "inMsgRecvQueue 执行,但没有上锁。。。。" << endl;
				//}

					

				//my_mutex2.unlock();				//顺序无所谓
				//my_mutex1.unlock();

			}
		}
	}


	bool outMsgLULproc(int &command)
	{
		//lock_guard<mutex>sbguard(my_mutex1);       //sbguard是对象名
		//my_mutex1.lock();
		//my_mutex2.lock();
		//std::lock(my_mutex1, my_mutex2);

	

		std::unique_lock<std::mutex>sbguard1(my_mutex1);
		std::chrono::milliseconds dura(100);    //等待20s
		std::this_thread::sleep_for(dura);		  //休息一定时长  outMsgLULproc先跑起来

		if (!msgRecvQueue.empty())
		{
		    command = msgRecvQueue.front();      //返回第一个元素但不检查元素是否存在
			msgRecvQueue.pop_front();            //移除第一个元素但不返回
			//处理数据。。。。。
			/*my_mutex1.unlock();
			my_mutex2.unlock();*/
			return true;
		}	
		else
		{
			//my_mutex1.unlock();
			//my_mutex2.unlock();
			return false; 
		}
	
	}

	//读取命令的线程
	void outMsgRecvQueue()
	{
		int command = 0;	
		for (int i = 0; i < 100000; i++)
		{
			bool result = outMsgLULproc(command);
			if (result == true)
			{
			
				cout << "outMsgRecvQueue 执行,取出一个元素" << command << endl;
				//数据处理
			}

		
		}
		cout << "end" << endl;
	
	}
private:

	std::list<int>msgRecvQueue;
	mutex my_mutex1;           //创建一个互斥量
	mutex my_mutex2;     

};

int main()
{
 //   //一 创建线程和等待多个线程
	//vector<thread>mythreads;
	创建10个线程,线程入口函数统一使用 myprint
	1)多线程执行顺序是乱的
	2)这种join写法更容易写出稳定程序
	3)把thread对象放入到容器里,对管理大量线程有帮助
	//for (int i = 0; i < 10; i++)
	//{
	//	//创建10个线程,已经开始执行
	//	mythreads.push_back(thread(Myprint, i));
	//
	//}
	//for (auto iter = mythreads.begin(); iter != mythreads.end(); ++iter)
	//{
	//	iter->join();
	//}
	//cout << "I LOVE CHINA" << endl;

	//二 数据共享
	//2.1只读数据
	//2.2 有读有写
	//最简单的不崩溃处理 读和写不能同时进行
	//2.3其他案例
	//数据共享:    
	
	//三 共享数据的保护案例代码
	//网络游戏服务器,有两个自己创建的线程,一个线程手机玩家命令(数字表示),并把名利数据写入到一个队列中。
	//另一个线程从队列中取出玩家发送来的命令,解析,然后执行玩家的动作。
	//使用list,频繁的按顺序插入和删除时效率较高


	//用成员函数作为线程函数的方法写线程

	A myobja;
	thread myOutnMsg(&A::outMsgRecvQueue, &myobja);
	thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
	myOutnMsg.join();
	myInMsgObj.join();

	//四 互斥量的概念
	//步骤:先lock 操作共享数据 然后unlock
	//lock和unlock要成对使用。有lock忘记unlock的问题非常难排查
	//为了防止忘记unlock(),引入了一个叫std::lock_guard的类模板
	//智能指针(unique_ptr<>)

	//std::lock_guard类模板 直接取代lock()和unlock(),用了类模板不能再用lock和unlock
	//要将保护量放在lock和unlock里


	//五 死锁
	/*
	**死锁问题的前提条件是:有至少两个锁,即至少两个互斥量,金锁(Jinlock),银锁(Yinlock)**
	两个线程A,B
	线程A执行时,先锁金锁,然后去锁银锁
	两个线程出现了上下文切换,线程B执行了,线程B先锁银锁,因为银锁没有被A锁上,所以被B锁上了,然后线程B去锁金锁
	此时产生了死锁
	线程A锁不了银锁,流程走不下去
	线程B锁不了金锁,流程走不下去
	死锁产生的关键是两个互斥量的上锁顺序不一致
	*/

	//5.1死锁演示
	//5.2死锁的一般解决方案
	//只要保证两个互斥量上锁的顺序一致,就不会造成死锁。


	//5.3 std::lock()函数模板
	//能力:一次锁住两个或者两个以上的互斥量(至少两个,多了不限,一个不行)
	//不存在因为锁头的顺序问题导致出现死锁问题。
	//如果互斥量中有一个没锁住,则等待,等待所有互斥量否锁住,才能继续往下走
	//特点:要么两个互斥量都锁住,要么两个互斥量都没锁住。如果只锁了一个,另外一个没成功,则立即解锁已经锁住的。
	//用来处理多个互斥量的情况

	//5.4 std:lock_guard的std:adopt_lock参数


	//六 unique_lock取代lock_guard
	//unique_lock是个类模板,工作中推荐使用lock_guard
	//lock_guard取代了mutex的lock和unlock
	//unique_lock比lock_guard灵活,效率略差,内存占用稍多。

	//七 unique_lock的第二个参数

	// 7.1 adopt_lock
	//lock_guard中的adopt_lock起标记作用 表示互斥量已经被lock了,在写这个标记前需要把互斥量lock 否则报错
	//adopt的效果是:假设调用方 线程已经拥有了互斥的所有权(已经lock成功了)
	//unique_lock也有adopt_lock标记,含义相同。
	//灵活性:
	//对于同一个锁 如果其中一个线程上了锁,则另一个也需要这个锁的线程就会处于等待状态,知道前一个线程解锁,会浪费很多时间
	

	//7.2 std::try to lock
	//尝试用mutex的lock()去锁定lock,但如果没有锁定成功,也会立即返回,并不会阻塞
	//用try to lock的前提是不可以手动上锁,否则异常
	

	//7.3 std::defer_lock
	//使用defer_lock的前提是 不能自己先lock,否则异常,与try to lock一样,与adopt_lock不同
	//defer_lock  初始化一个没有加锁的mutex
	//借着defer_lock的话题,介绍一些unique_lock的重要成员函数


	//八 unique_lcok的成员函数
	// 8.1 lock()

}


在这里插入图片描述

3.2 unlock()

解锁:前提是已经加锁

// 并发与多线程2_4.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

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


//vector<int>g_v = { 1,2,3 };
//
//void Myprint(int inum)
//{
//	cout << "myprint线程开始执行了,线程编号" << inum << endl;
//	cout << "myprint线程结束执行了,线程编号" << inum << endl;
//	cout << "id 为" << std::this_thread::get_id() << "打印g_v值" << g_v[0] << g_v[1] << g_v[2] << endl;
//
//}



class A
{
public:
	//把玩家命令放入到一个队列的进程
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			//my_mutex.lock();
			
			{


				//my_mutex1.lock();		//先锁金锁 实际中两个lock间会执行其他的东西
				//my_mutex2.lock();		//再锁银锁
				//使用std::lock()
				//std::lock(my_mutex1, my_mutex2);    //相当与每个互斥量都调用了lock
				//std::lock_guard<mutex>sbguard1(my_mutex1, adopt_lock);
				//std::lock_guard<mutex>sbguard2(my_mutex2, adopt_lock);
				//std::unique_lock<std::mutex>sbguard1(my_mutex1);

				//my_mutex1.lock();                     //in线程后到
				//std::unique_lock<std::mutex>sbguard1(my_mutex1,adopt_lock);  //使用了adopt_lock 需要提前lock

				//尝试加锁
				//std::unique_lock<std::mutex>sbguard1(my_mutex1, std::try_to_lock);         //使用此参数的条件时互斥量没有上锁

				//if (sbguard1.owns_lock())              //如果尝试上锁成功
				//{
					cout << "inMsgRecvQueue执行,插入一个元素" << i << endl;

					//创建没有加锁的my_mutex1
					std::unique_lock<std::mutex>sbguard1(my_mutex1, std::defer_lock);

					sbguard1.lock();              //不需要手动解锁

					//处理共享代码

					sbguard1.unlock();              //不需要手动解锁
					//处理非共享代码

					sbguard1.lock();              //不需要手动解锁
					//处理共享代码

					msgRecvQueue.push_back(i);     //假设数字i为命令 放入队列

				//}
				//else								  // 如果上锁失败
				//{
				//	cout << "inMsgRecvQueue 执行,但没有上锁。。。。" << endl;
				//}

					

				//my_mutex2.unlock();				//顺序无所谓
				//my_mutex1.unlock();

			}
		}
	}


	bool outMsgLULproc(int &command)
	{
		//lock_guard<mutex>sbguard(my_mutex1);       //sbguard是对象名
		//my_mutex1.lock();
		//my_mutex2.lock();
		//std::lock(my_mutex1, my_mutex2);

	

		std::unique_lock<std::mutex>sbguard1(my_mutex1);
		std::chrono::milliseconds dura(100);    //等待20s
		std::this_thread::sleep_for(dura);		  //休息一定时长  outMsgLULproc先跑起来

		if (!msgRecvQueue.empty())
		{
		    command = msgRecvQueue.front();      //返回第一个元素但不检查元素是否存在
			msgRecvQueue.pop_front();            //移除第一个元素但不返回
			//处理数据。。。。。
			/*my_mutex1.unlock();
			my_mutex2.unlock();*/
			return true;
		}	
		else
		{
			//my_mutex1.unlock();
			//my_mutex2.unlock();
			return false; 
		}
	
	}

	//读取命令的线程
	void outMsgRecvQueue()
	{
		int command = 0;	
		for (int i = 0; i < 100000; i++)
		{
			bool result = outMsgLULproc(command);
			if (result == true)
			{
			
				cout << "outMsgRecvQueue 执行,取出一个元素" << command << endl;
				//数据处理
			}

		
		}
		cout << "end" << endl;
	
	}
private:

	std::list<int>msgRecvQueue;
	mutex my_mutex1;           //创建一个互斥量
	mutex my_mutex2;     

};

int main()
{
 //   //一 创建线程和等待多个线程
	//vector<thread>mythreads;
	创建10个线程,线程入口函数统一使用 myprint
	1)多线程执行顺序是乱的
	2)这种join写法更容易写出稳定程序
	3)把thread对象放入到容器里,对管理大量线程有帮助
	//for (int i = 0; i < 10; i++)
	//{
	//	//创建10个线程,已经开始执行
	//	mythreads.push_back(thread(Myprint, i));
	//
	//}
	//for (auto iter = mythreads.begin(); iter != mythreads.end(); ++iter)
	//{
	//	iter->join();
	//}
	//cout << "I LOVE CHINA" << endl;

	//二 数据共享
	//2.1只读数据
	//2.2 有读有写
	//最简单的不崩溃处理 读和写不能同时进行
	//2.3其他案例
	//数据共享:    
	
	//三 共享数据的保护案例代码
	//网络游戏服务器,有两个自己创建的线程,一个线程手机玩家命令(数字表示),并把名利数据写入到一个队列中。
	//另一个线程从队列中取出玩家发送来的命令,解析,然后执行玩家的动作。
	//使用list,频繁的按顺序插入和删除时效率较高


	//用成员函数作为线程函数的方法写线程

	A myobja;
	thread myOutnMsg(&A::outMsgRecvQueue, &myobja);
	thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
	myOutnMsg.join();
	myInMsgObj.join();

	//四 互斥量的概念
	//步骤:先lock 操作共享数据 然后unlock
	//lock和unlock要成对使用。有lock忘记unlock的问题非常难排查
	//为了防止忘记unlock(),引入了一个叫std::lock_guard的类模板
	//智能指针(unique_ptr<>)

	//std::lock_guard类模板 直接取代lock()和unlock(),用了类模板不能再用lock和unlock
	//要将保护量放在lock和unlock里


	//五 死锁
	/*
	**死锁问题的前提条件是:有至少两个锁,即至少两个互斥量,金锁(Jinlock),银锁(Yinlock)**
	两个线程A,B
	线程A执行时,先锁金锁,然后去锁银锁
	两个线程出现了上下文切换,线程B执行了,线程B先锁银锁,因为银锁没有被A锁上,所以被B锁上了,然后线程B去锁金锁
	此时产生了死锁
	线程A锁不了银锁,流程走不下去
	线程B锁不了金锁,流程走不下去
	死锁产生的关键是两个互斥量的上锁顺序不一致
	*/

	//5.1死锁演示
	//5.2死锁的一般解决方案
	//只要保证两个互斥量上锁的顺序一致,就不会造成死锁。


	//5.3 std::lock()函数模板
	//能力:一次锁住两个或者两个以上的互斥量(至少两个,多了不限,一个不行)
	//不存在因为锁头的顺序问题导致出现死锁问题。
	//如果互斥量中有一个没锁住,则等待,等待所有互斥量否锁住,才能继续往下走
	//特点:要么两个互斥量都锁住,要么两个互斥量都没锁住。如果只锁了一个,另外一个没成功,则立即解锁已经锁住的。
	//用来处理多个互斥量的情况

	//5.4 std:lock_guard的std:adopt_lock参数


	//六 unique_lock取代lock_guard
	//unique_lock是个类模板,工作中推荐使用lock_guard
	//lock_guard取代了mutex的lock和unlock
	//unique_lock比lock_guard灵活,效率略差,内存占用稍多。

	//七 unique_lock的第二个参数

	// 7.1 adopt_lock
	//lock_guard中的adopt_lock起标记作用 表示互斥量已经被lock了,在写这个标记前需要把互斥量lock 否则报错
	//adopt的效果是:假设调用方 线程已经拥有了互斥的所有权(已经lock成功了)
	//unique_lock也有adopt_lock标记,含义相同。
	//灵活性:
	//对于同一个锁 如果其中一个线程上了锁,则另一个也需要这个锁的线程就会处于等待状态,知道前一个线程解锁,会浪费很多时间
	

	//7.2 std::try to lock
	//尝试用mutex的lock()去锁定lock,但如果没有锁定成功,也会立即返回,并不会阻塞
	//用try to lock的前提是不可以手动上锁,否则异常
	

	//7.3 std::defer_lock
	//使用defer_lock的前提是 不能自己先lock,否则异常,与try to lock一样,与adopt_lock不同
	//defer_lock  初始化一个没有加锁的mutex
	//借着defer_lock的话题,介绍一些unique_lock的重要成员函数


	//八 unique_lcok的成员函数

	// 8.1 lock()

	//8.2 unlock()  希望处理些非共享代码,处理非共享代码,然后再lock上
	//即unique_lock通过成员函数可以实现手动上锁,自动解锁和手动解锁

}


程序稳定运行
在这里插入图片描述

3.3 try_lock

与defer_lock配合使用

// 并发与多线程2_4.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

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


//vector<int>g_v = { 1,2,3 };
//
//void Myprint(int inum)
//{
//	cout << "myprint线程开始执行了,线程编号" << inum << endl;
//	cout << "myprint线程结束执行了,线程编号" << inum << endl;
//	cout << "id 为" << std::this_thread::get_id() << "打印g_v值" << g_v[0] << g_v[1] << g_v[2] << endl;
//
//}



class A
{
public:
	//把玩家命令放入到一个队列的进程
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			//my_mutex.lock();
			
			{


				//my_mutex1.lock();		//先锁金锁 实际中两个lock间会执行其他的东西
				//my_mutex2.lock();		//再锁银锁
				//使用std::lock()
				//std::lock(my_mutex1, my_mutex2);    //相当与每个互斥量都调用了lock
				//std::lock_guard<mutex>sbguard1(my_mutex1, adopt_lock);
				//std::lock_guard<mutex>sbguard2(my_mutex2, adopt_lock);
				//std::unique_lock<std::mutex>sbguard1(my_mutex1);

				//my_mutex1.lock();                     //in线程后到
				//std::unique_lock<std::mutex>sbguard1(my_mutex1,adopt_lock);  //使用了adopt_lock 需要提前lock

				//尝试加锁
				//std::unique_lock<std::mutex>sbguard1(my_mutex1, std::try_to_lock);         //使用此参数的条件时互斥量没有上锁

				//if (sbguard1.owns_lock())              //如果尝试上锁成功
				//{
					

					//创建没有加锁的my_mutex1
					std::unique_lock<std::mutex>sbguard1(my_mutex1, std::defer_lock);

					//sbguard1.lock();              //不需要手动解锁

					//处理共享代码

					//sbguard1.unlock();              //不需要手动解锁
					//处理非共享代码

					//sbguard1.lock();              //不需要手动解锁
					//处理共享代码

					if (sbguard1.try_lock() == true)
					{
						cout << "inMsgRecvQueue执行,插入一个元素" << i << endl;
						msgRecvQueue.push_back(i);     //假设数字i为命令 放入队列
						
					}
					else
					{
						cout << "inMsgRecvQueue执行,上锁失败" << endl;
						
					}

					

				//}
				//else								  // 如果上锁失败
				//{
				//	cout << "inMsgRecvQueue 执行,但没有上锁。。。。" << endl;
				//}

					

				//my_mutex2.unlock();				//顺序无所谓
				//my_mutex1.unlock();

			}
		}
	}


	bool outMsgLULproc(int &command)
	{
		//lock_guard<mutex>sbguard(my_mutex1);       //sbguard是对象名
		//my_mutex1.lock();
		//my_mutex2.lock();
		//std::lock(my_mutex1, my_mutex2);

	

		std::unique_lock<std::mutex>sbguard1(my_mutex1);
		std::chrono::milliseconds dura(200);    //等待200ms
		std::this_thread::sleep_for(dura);		  //休息一定时长  outMsgLULproc先跑起来

		if (!msgRecvQueue.empty())
		{
		    command = msgRecvQueue.front();      //返回第一个元素但不检查元素是否存在
			msgRecvQueue.pop_front();            //移除第一个元素但不返回
			//处理数据。。。。。
			/*my_mutex1.unlock();
			my_mutex2.unlock();*/
			return true;
		}	
		else
		{
			//my_mutex1.unlock();
			//my_mutex2.unlock();
			return false; 
		}
	
	}

	//读取命令的线程
	void outMsgRecvQueue()
	{
		int command = 0;	
		for (int i = 0; i < 100000; i++)
		{
			bool result = outMsgLULproc(command);
			if (result == true)
			{
			
				cout << "outMsgRecvQueue 执行,取出一个元素" << command << endl;
				//数据处理
			}

		
		}
		cout << "end" << endl;
	
	}
private:

	std::list<int>msgRecvQueue;
	mutex my_mutex1;           //创建一个互斥量
	mutex my_mutex2;     

};

int main()
{
 //   //一 创建线程和等待多个线程
	//vector<thread>mythreads;
	创建10个线程,线程入口函数统一使用 myprint
	1)多线程执行顺序是乱的
	2)这种join写法更容易写出稳定程序
	3)把thread对象放入到容器里,对管理大量线程有帮助
	//for (int i = 0; i < 10; i++)
	//{
	//	//创建10个线程,已经开始执行
	//	mythreads.push_back(thread(Myprint, i));
	//
	//}
	//for (auto iter = mythreads.begin(); iter != mythreads.end(); ++iter)
	//{
	//	iter->join();
	//}
	//cout << "I LOVE CHINA" << endl;

	//二 数据共享
	//2.1只读数据
	//2.2 有读有写
	//最简单的不崩溃处理 读和写不能同时进行
	//2.3其他案例
	//数据共享:    
	
	//三 共享数据的保护案例代码
	//网络游戏服务器,有两个自己创建的线程,一个线程手机玩家命令(数字表示),并把名利数据写入到一个队列中。
	//另一个线程从队列中取出玩家发送来的命令,解析,然后执行玩家的动作。
	//使用list,频繁的按顺序插入和删除时效率较高


	//用成员函数作为线程函数的方法写线程

	A myobja;
	thread myOutnMsg(&A::outMsgRecvQueue, &myobja);
	thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
	myOutnMsg.join();
	myInMsgObj.join();

	//四 互斥量的概念
	//步骤:先lock 操作共享数据 然后unlock
	//lock和unlock要成对使用。有lock忘记unlock的问题非常难排查
	//为了防止忘记unlock(),引入了一个叫std::lock_guard的类模板
	//智能指针(unique_ptr<>)

	//std::lock_guard类模板 直接取代lock()和unlock(),用了类模板不能再用lock和unlock
	//要将保护量放在lock和unlock里


	//五 死锁
	/*
	**死锁问题的前提条件是:有至少两个锁,即至少两个互斥量,金锁(Jinlock),银锁(Yinlock)**
	两个线程A,B
	线程A执行时,先锁金锁,然后去锁银锁
	两个线程出现了上下文切换,线程B执行了,线程B先锁银锁,因为银锁没有被A锁上,所以被B锁上了,然后线程B去锁金锁
	此时产生了死锁
	线程A锁不了银锁,流程走不下去
	线程B锁不了金锁,流程走不下去
	死锁产生的关键是两个互斥量的上锁顺序不一致
	*/

	//5.1死锁演示
	//5.2死锁的一般解决方案
	//只要保证两个互斥量上锁的顺序一致,就不会造成死锁。


	//5.3 std::lock()函数模板
	//能力:一次锁住两个或者两个以上的互斥量(至少两个,多了不限,一个不行)
	//不存在因为锁头的顺序问题导致出现死锁问题。
	//如果互斥量中有一个没锁住,则等待,等待所有互斥量否锁住,才能继续往下走
	//特点:要么两个互斥量都锁住,要么两个互斥量都没锁住。如果只锁了一个,另外一个没成功,则立即解锁已经锁住的。
	//用来处理多个互斥量的情况

	//5.4 std:lock_guard的std:adopt_lock参数


	//六 unique_lock取代lock_guard
	//unique_lock是个类模板,工作中推荐使用lock_guard
	//lock_guard取代了mutex的lock和unlock
	//unique_lock比lock_guard灵活,效率略差,内存占用稍多。

	//七 unique_lock的第二个参数

	// 7.1 adopt_lock
	//lock_guard中的adopt_lock起标记作用 表示互斥量已经被lock了,在写这个标记前需要把互斥量lock 否则报错
	//adopt的效果是:假设调用方 线程已经拥有了互斥的所有权(已经lock成功了)
	//unique_lock也有adopt_lock标记,含义相同。
	//灵活性:
	//对于同一个锁 如果其中一个线程上了锁,则另一个也需要这个锁的线程就会处于等待状态,知道前一个线程解锁,会浪费很多时间
	

	//7.2 std::try_to_lock
	//尝试用mutex的lock()去锁定lock,但如果没有锁定成功,也会立即返回,并不会阻塞
	//用try_to_lock的前提是不可以手动上锁,否则异常
	

	//7.3 std::defer_lock
	//使用defer_lock的前提是 不能自己先lock,否则异常,与try to lock一样,与adopt_lock不同
	//defer_lock  初始化一个没有加锁的mutex
	//借着defer_lock的话题,介绍一些unique_lock的重要成员函数


	//八 unique_lcok的成员函数

	// 8.1 lock()

	//8.2 unlock()  希望处理些非共享代码,处理非共享代码,然后再lock上
	//即unique_lock通过成员函数可以实现手动上锁,自动解锁和手动解锁
	//也可以自己在程序结尾自己家unlock  unique_lock会作出判断

	//8.3 try_lock 尝试给互斥量加锁,如果拿不到锁,则返回false,拿到了则返回true,程序不阻塞

}


在这里插入图片描述

3.4 release()

// 并发与多线程2_4.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

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


//vector<int>g_v = { 1,2,3 };
//
//void Myprint(int inum)
//{
//	cout << "myprint线程开始执行了,线程编号" << inum << endl;
//	cout << "myprint线程结束执行了,线程编号" << inum << endl;
//	cout << "id 为" << std::this_thread::get_id() << "打印g_v值" << g_v[0] << g_v[1] << g_v[2] << endl;
//
//}



class A
{
public:
	//把玩家命令放入到一个队列的进程
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			//my_mutex.lock();
			
			{


				//my_mutex1.lock();		//先锁金锁 实际中两个lock间会执行其他的东西
				//my_mutex2.lock();		//再锁银锁
				//使用std::lock()
				//std::lock(my_mutex1, my_mutex2);    //相当与每个互斥量都调用了lock
				//std::lock_guard<mutex>sbguard1(my_mutex1, adopt_lock);
				//std::lock_guard<mutex>sbguard2(my_mutex2, adopt_lock);
				//std::unique_lock<std::mutex>sbguard1(my_mutex1);

				//my_mutex1.lock();                     //in线程后到
				//std::unique_lock<std::mutex>sbguard1(my_mutex1,adopt_lock);  //使用了adopt_lock 需要提前lock

				//尝试加锁
				//std::unique_lock<std::mutex>sbguard1(my_mutex1, std::try_to_lock);         //使用此参数的条件时互斥量没有上锁

				//if (sbguard1.owns_lock())              //如果尝试上锁成功
				//{
					

					//创建没有加锁的my_mutex1
					//std::unique_lock<std::mutex>sbguard1(my_mutex1, std::defer_lock);

					//sbguard1.lock();              //不需要手动解锁

					//处理共享代码

					//sbguard1.unlock();              //不需要手动解锁
					//处理非共享代码

					//sbguard1.lock();              //不需要手动解锁
					//处理共享代码
					
					std::unique_lock<std::mutex>sbguard1(my_mutex1);		// 创建一个加锁的mutex
					std::mutex *ptx = sbguard1.release();					//将sbguard1与my_mutex1解绑  手动解锁
					//if (sbguard1.try_lock() == true)
					//{
						cout << "inMsgRecvQueue执行,插入一个元素" << i << endl;
						msgRecvQueue.push_back(i);     //假设数字i为命令 放入队列

						ptx->unlock();						//手动解锁
				
					//}
					//else
					//{
					//	cout << "inMsgRecvQueue执行,上锁失败" << endl;
					//	
					//}

					

				//}
				//else								  // 如果上锁失败
				//{
				//	cout << "inMsgRecvQueue 执行,但没有上锁。。。。" << endl;
				//}

					

				//my_mutex2.unlock();				//顺序无所谓
				//my_mutex1.unlock();

			}
		}
	}


	bool outMsgLULproc(int &command)
	{
		//lock_guard<mutex>sbguard(my_mutex1);       //sbguard是对象名
		//my_mutex1.lock();
		//my_mutex2.lock();
		//std::lock(my_mutex1, my_mutex2);

	

		std::unique_lock<std::mutex>sbguard1(my_mutex1);
		std::chrono::milliseconds dura(10);    //等待10ms
		std::this_thread::sleep_for(dura);		  //休息一定时长  outMsgLULproc先跑起来

		if (!msgRecvQueue.empty())
		{
		    command = msgRecvQueue.front();      //返回第一个元素但不检查元素是否存在
			msgRecvQueue.pop_front();            //移除第一个元素但不返回
			//处理数据。。。。。
			/*my_mutex1.unlock();
			my_mutex2.unlock();*/
			return true;
		}	
		else
		{
			//my_mutex1.unlock();
			//my_mutex2.unlock();
			return false; 
		}
	
	}

	//读取命令的线程
	void outMsgRecvQueue()
	{
		int command = 0;	
		for (int i = 0; i < 100000; i++)
		{
			bool result = outMsgLULproc(command);
			if (result == true)
			{
			
				cout << "outMsgRecvQueue 执行,取出一个元素" << command << endl;
				//数据处理
			}

		
		}
		cout << "end" << endl;
	
	}
private:

	std::list<int>msgRecvQueue;
	mutex my_mutex1;           //创建一个互斥量
	mutex my_mutex2;     

};

int main()
{
 //   //一 创建线程和等待多个线程
	//vector<thread>mythreads;
	创建10个线程,线程入口函数统一使用 myprint
	1)多线程执行顺序是乱的
	2)这种join写法更容易写出稳定程序
	3)把thread对象放入到容器里,对管理大量线程有帮助
	//for (int i = 0; i < 10; i++)
	//{
	//	//创建10个线程,已经开始执行
	//	mythreads.push_back(thread(Myprint, i));
	//
	//}
	//for (auto iter = mythreads.begin(); iter != mythreads.end(); ++iter)
	//{
	//	iter->join();
	//}
	//cout << "I LOVE CHINA" << endl;

	//二 数据共享
	//2.1只读数据
	//2.2 有读有写
	//最简单的不崩溃处理 读和写不能同时进行
	//2.3其他案例
	//数据共享:    
	
	//三 共享数据的保护案例代码
	//网络游戏服务器,有两个自己创建的线程,一个线程手机玩家命令(数字表示),并把名利数据写入到一个队列中。
	//另一个线程从队列中取出玩家发送来的命令,解析,然后执行玩家的动作。
	//使用list,频繁的按顺序插入和删除时效率较高


	//用成员函数作为线程函数的方法写线程

	A myobja;
	thread myOutnMsg(&A::outMsgRecvQueue, &myobja);
	thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
	myOutnMsg.join();
	myInMsgObj.join();

	//四 互斥量的概念
	//步骤:先lock 操作共享数据 然后unlock
	//lock和unlock要成对使用。有lock忘记unlock的问题非常难排查
	//为了防止忘记unlock(),引入了一个叫std::lock_guard的类模板
	//智能指针(unique_ptr<>)

	//std::lock_guard类模板 直接取代lock()和unlock(),用了类模板不能再用lock和unlock
	//要将保护量放在lock和unlock里


	//五 死锁
	/*
	**死锁问题的前提条件是:有至少两个锁,即至少两个互斥量,金锁(Jinlock),银锁(Yinlock)**
	两个线程A,B
	线程A执行时,先锁金锁,然后去锁银锁
	两个线程出现了上下文切换,线程B执行了,线程B先锁银锁,因为银锁没有被A锁上,所以被B锁上了,然后线程B去锁金锁
	此时产生了死锁
	线程A锁不了银锁,流程走不下去
	线程B锁不了金锁,流程走不下去
	死锁产生的关键是两个互斥量的上锁顺序不一致
	*/

	//5.1死锁演示
	//5.2死锁的一般解决方案
	//只要保证两个互斥量上锁的顺序一致,就不会造成死锁。


	//5.3 std::lock()函数模板
	//能力:一次锁住两个或者两个以上的互斥量(至少两个,多了不限,一个不行)
	//不存在因为锁头的顺序问题导致出现死锁问题。
	//如果互斥量中有一个没锁住,则等待,等待所有互斥量否锁住,才能继续往下走
	//特点:要么两个互斥量都锁住,要么两个互斥量都没锁住。如果只锁了一个,另外一个没成功,则立即解锁已经锁住的。
	//用来处理多个互斥量的情况

	//5.4 std:lock_guard的std:adopt_lock参数


	//六 unique_lock取代lock_guard
	//unique_lock是个类模板,工作中推荐使用lock_guard
	//lock_guard取代了mutex的lock和unlock
	//unique_lock比lock_guard灵活,效率略差,内存占用稍多。

	//七 unique_lock的第二个参数

	// 7.1 adopt_lock
	//lock_guard中的adopt_lock起标记作用 表示互斥量已经被lock了,在写这个标记前需要把互斥量lock 否则报错
	//adopt的效果是:假设调用方 线程已经拥有了互斥的所有权(已经lock成功了)
	//unique_lock也有adopt_lock标记,含义相同。
	//灵活性:
	//对于同一个锁 如果其中一个线程上了锁,则另一个也需要这个锁的线程就会处于等待状态,知道前一个线程解锁,会浪费很多时间
	

	//7.2 std::try_to_lock
	//尝试用mutex的lock()去锁定lock,但如果没有锁定成功,也会立即返回,并不会阻塞
	//用try_to_lock的前提是不可以手动上锁,否则异常
	

	//7.3 std::defer_lock
	//使用defer_lock的前提是 不能自己先lock,否则异常,与try to lock一样,与adopt_lock不同
	//defer_lock  初始化一个没有加锁的mutex
	//借着defer_lock的话题,介绍一些unique_lock的重要成员函数


	//八 unique_lcok的成员函数

	// 8.1 lock()

	//8.2 unlock()  希望处理些非共享代码,处理非共享代码,然后再lock上
	//即unique_lock通过成员函数可以实现手动上锁,自动解锁和手动解锁
	//也可以自己在程序结尾自己家unlock  unique_lock会作出判断

	//8.3 try_lock 尝试给互斥量加锁,如果拿不到锁,则返回false,拿到了则返回true,程序不阻塞


	//8.4 release(),返回它管理的mutex对象指针,并释放所有权,也就是说,这个unique_lock和mutex不再有关系
	//严格区分unlock()和release()的区别,不要混淆
	//如果原来mutex对象处于加锁状态,如果release了,则需要手动解锁
	//release返回原始的mutex指针
	//为什么有时候需要unlock():因为lock锁住的代码段越少,执行越快,整个程序运行效率越高
	//乘互斥量锁住的代码多少,称为粒度 ,粒度一般用粗细描述
	//要选择合适粒度的代码来保护共享数据,粒度太细影响效率,粒度太粗影响效率
	
}


在这里插入图片描述

4.unique_lock所有权的传递

一个mutex只和一个mutex绑定在一起
所有权概念: sbguard1拥有my_mutex1的所有权,sbguard1可以把对my_mutex1的所有权转一个其他unique_lock对象,
所以,unique_lock的所有权,可以转移,不能复制。
std::unique_lockstd::mutexsbguard1(my_mutex1);
std::unique_lockstd::mutexsbguard2(sbguard1); 这样是非法的
std::unique_lockstd::mutexsbguard2(std::move(sbguard1)); //将sbguard1的所有权转移给sbguard2

4.1 调用std::move转移所有权

// 并发与多线程2_4.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。


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


//vector<int>g_v = { 1,2,3 };
//
//void Myprint(int inum)
//{
//	cout << "myprint线程开始执行了,线程编号" << inum << endl;
//	cout << "myprint线程结束执行了,线程编号" << inum << endl;
//	cout << "id 为" << std::this_thread::get_id() << "打印g_v值" << g_v[0] << g_v[1] << g_v[2] << endl;
//
//}



class A
{
public:
	//把玩家命令放入到一个队列的进程
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			//my_mutex.lock();
			
			{


				//my_mutex1.lock();		//先锁金锁 实际中两个lock间会执行其他的东西
				//my_mutex2.lock();		//再锁银锁
				//使用std::lock()
				//std::lock(my_mutex1, my_mutex2);    //相当与每个互斥量都调用了lock
				//std::lock_guard<mutex>sbguard1(my_mutex1, adopt_lock);
				//std::lock_guard<mutex>sbguard2(my_mutex2, adopt_lock);
				//std::unique_lock<std::mutex>sbguard1(my_mutex1);

				//my_mutex1.lock();                     //in线程后到
				//std::unique_lock<std::mutex>sbguard1(my_mutex1,adopt_lock);  //使用了adopt_lock 需要提前lock

				//尝试加锁
				//std::unique_lock<std::mutex>sbguard1(my_mutex1, std::try_to_lock);         //使用此参数的条件时互斥量没有上锁

				//if (sbguard1.owns_lock())              //如果尝试上锁成功
				//{
					

					//创建没有加锁的my_mutex1
					//std::unique_lock<std::mutex>sbguard1(my_mutex1, std::defer_lock);

					//sbguard1.lock();              //不需要手动解锁

					//处理共享代码

					//sbguard1.unlock();              //不需要手动解锁
					//处理非共享代码

					//sbguard1.lock();              //不需要手动解锁
					//处理共享代码
					
					std::unique_lock<std::mutex>sbguard1(my_mutex1);		// 创建一个加锁的mutex
					std::unique_lock<std::mutex>sbguard2(std::move(sbguard1)); //将sbguard1的所有权转移给sbguard2
					//现在sbguard1指向空 sbguard2指向了my_mutex1
					/*std::mutex *ptx = sbguard1.release();	*/				//将sbguard1与my_mutex1解绑  手动解锁
					//if (sbguard1.try_lock() == true)
					//{
						cout << "inMsgRecvQueue执行,插入一个元素" << i << endl;
						msgRecvQueue.push_back(i);     //假设数字i为命令 放入队列

						sbguard2.unlock();						//手动解锁
				
					//}
					//else
					//{
					//	cout << "inMsgRecvQueue执行,上锁失败" << endl;
					//	
					//}

					

				//}
				//else								  // 如果上锁失败
				//{
				//	cout << "inMsgRecvQueue 执行,但没有上锁。。。。" << endl;
				//}

					

				//my_mutex2.unlock();				//顺序无所谓
				//my_mutex1.unlock();

			}
		}
	}


	bool outMsgLULproc(int &command)
	{
		//lock_guard<mutex>sbguard(my_mutex1);       //sbguard是对象名
		//my_mutex1.lock();
		//my_mutex2.lock();
		//std::lock(my_mutex1, my_mutex2);

	

		std::unique_lock<std::mutex>sbguard1(my_mutex1);
		std::chrono::milliseconds dura(10);    //等待10ms
		std::this_thread::sleep_for(dura);		  //休息一定时长  outMsgLULproc先跑起来

		if (!msgRecvQueue.empty())
		{
		    command = msgRecvQueue.front();      //返回第一个元素但不检查元素是否存在
			msgRecvQueue.pop_front();            //移除第一个元素但不返回
			//处理数据。。。。。
			/*my_mutex1.unlock();
			my_mutex2.unlock();*/
			return true;
		}	
		else
		{
			//my_mutex1.unlock();
			//my_mutex2.unlock();
			return false; 
		}
	
	}

	//读取命令的线程
	void outMsgRecvQueue()
	{
		int command = 0;	
		for (int i = 0; i < 100000; i++)
		{
			bool result = outMsgLULproc(command);
			if (result == true)
			{
			
				cout << "outMsgRecvQueue 执行,取出一个元素" << command << endl;
				//数据处理
			}

		
		}
		cout << "end" << endl;
	
	}
private:

	std::list<int>msgRecvQueue;
	mutex my_mutex1;           //创建一个互斥量
	mutex my_mutex2;     

};

int main()
{
 //   //一 创建线程和等待多个线程
	//vector<thread>mythreads;
	创建10个线程,线程入口函数统一使用 myprint
	1)多线程执行顺序是乱的
	2)这种join写法更容易写出稳定程序
	3)把thread对象放入到容器里,对管理大量线程有帮助
	//for (int i = 0; i < 10; i++)
	//{
	//	//创建10个线程,已经开始执行
	//	mythreads.push_back(thread(Myprint, i));
	//
	//}
	//for (auto iter = mythreads.begin(); iter != mythreads.end(); ++iter)
	//{
	//	iter->join();
	//}
	//cout << "I LOVE CHINA" << endl;

	//二 数据共享
	//2.1只读数据
	//2.2 有读有写
	//最简单的不崩溃处理 读和写不能同时进行
	//2.3其他案例
	//数据共享:    
	
	//三 共享数据的保护案例代码
	//网络游戏服务器,有两个自己创建的线程,一个线程手机玩家命令(数字表示),并把名利数据写入到一个队列中。
	//另一个线程从队列中取出玩家发送来的命令,解析,然后执行玩家的动作。
	//使用list,频繁的按顺序插入和删除时效率较高


	//用成员函数作为线程函数的方法写线程

	A myobja;
	thread myOutnMsg(&A::outMsgRecvQueue, &myobja);
	thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
	myOutnMsg.join();
	myInMsgObj.join();

	//四 互斥量的概念
	//步骤:先lock 操作共享数据 然后unlock
	//lock和unlock要成对使用。有lock忘记unlock的问题非常难排查
	//为了防止忘记unlock(),引入了一个叫std::lock_guard的类模板
	//智能指针(unique_ptr<>)

	//std::lock_guard类模板 直接取代lock()和unlock(),用了类模板不能再用lock和unlock
	//要将保护量放在lock和unlock里


	//五 死锁
	/*
	**死锁问题的前提条件是:有至少两个锁,即至少两个互斥量,金锁(Jinlock),银锁(Yinlock)**
	两个线程A,B
	线程A执行时,先锁金锁,然后去锁银锁
	两个线程出现了上下文切换,线程B执行了,线程B先锁银锁,因为银锁没有被A锁上,所以被B锁上了,然后线程B去锁金锁
	此时产生了死锁
	线程A锁不了银锁,流程走不下去
	线程B锁不了金锁,流程走不下去
	死锁产生的关键是两个互斥量的上锁顺序不一致
	*/

	//5.1死锁演示
	//5.2死锁的一般解决方案
	//只要保证两个互斥量上锁的顺序一致,就不会造成死锁。


	//5.3 std::lock()函数模板
	//能力:一次锁住两个或者两个以上的互斥量(至少两个,多了不限,一个不行)
	//不存在因为锁头的顺序问题导致出现死锁问题。
	//如果互斥量中有一个没锁住,则等待,等待所有互斥量否锁住,才能继续往下走
	//特点:要么两个互斥量都锁住,要么两个互斥量都没锁住。如果只锁了一个,另外一个没成功,则立即解锁已经锁住的。
	//用来处理多个互斥量的情况

	//5.4 std:lock_guard的std:adopt_lock参数


	//六 unique_lock取代lock_guard
	//unique_lock是个类模板,工作中推荐使用lock_guard
	//lock_guard取代了mutex的lock和unlock
	//unique_lock比lock_guard灵活,效率略差,内存占用稍多。

	//七 unique_lock的第二个参数

	// 7.1 adopt_lock
	//lock_guard中的adopt_lock起标记作用 表示互斥量已经被lock了,在写这个标记前需要把互斥量lock 否则报错
	//adopt的效果是:假设调用方 线程已经拥有了互斥的所有权(已经lock成功了)
	//unique_lock也有adopt_lock标记,含义相同。
	//灵活性:
	//对于同一个锁 如果其中一个线程上了锁,则另一个也需要这个锁的线程就会处于等待状态,知道前一个线程解锁,会浪费很多时间
	

	//7.2 std::try_to_lock
	//尝试用mutex的lock()去锁定lock,但如果没有锁定成功,也会立即返回,并不会阻塞
	//用try_to_lock的前提是不可以手动上锁,否则异常
	

	//7.3 std::defer_lock
	//使用defer_lock的前提是 不能自己先lock,否则异常,与try to lock一样,与adopt_lock不同
	//defer_lock  初始化一个没有加锁的mutex
	//借着defer_lock的话题,介绍一些unique_lock的重要成员函数


	//八 unique_lcok的成员函数

	// 8.1 lock()

	//8.2 unlock()  希望处理些非共享代码,处理非共享代码,然后再lock上
	//即unique_lock通过成员函数可以实现手动上锁,自动解锁和手动解锁
	//也可以自己在程序结尾自己家unlock  unique_lock会作出判断

	//8.3 try_lock 尝试给互斥量加锁,如果拿不到锁,则返回false,拿到了则返回true,程序不阻塞


	//8.4 release(),返回它管理的mutex对象指针,并释放所有权,也就是说,这个unique_lock和mutex不再有关系
	//严格区分unlock()和release()的区别,不要混淆
	//如果原来mutex对象处于加锁状态,如果release了,则需要手动解锁
	//release返回原始的mutex指针
	//为什么有时候需要unlock():因为lock锁住的代码段越少,执行越快,整个程序运行效率越高
	//乘互斥量锁住的代码多少,称为粒度 ,粒度一般用粗细描述
	//要选择合适粒度的代码来保护共享数据,粒度太细影响效率,粒度太粗影响效率
	


	// 九 unique_lock的所有权传递
	// 一个mutex只和一个mutex绑定在一起  
	//所有权概念: sbguard1拥有my_mutex1的所有权,sbguard1可以把对my_mutex1的所有权转一个其他unique_lock对象,
	//所以,unique_lock的所有权,可以转移,不能复制。
	//std::unique_lock<std::mutex>sbguard1(my_mutex1);
	//std::unique_lock<std::mutex>sbguard2(sbguard1);  这样是非法的
	//std::unique_lock<std::mutex>sbguard2(std::move(sbguard1)); //将sbguard1的所有权转移给sbguard2
}


在这里插入图片描述
在这里插入图片描述

4.2 使用A的类成员函数返回unique_lock对象

// 并发与多线程2_4.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

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


//vector<int>g_v = { 1,2,3 };
//
//void Myprint(int inum)
//{
//	cout << "myprint线程开始执行了,线程编号" << inum << endl;
//	cout << "myprint线程结束执行了,线程编号" << inum << endl;
//	cout << "id 为" << std::this_thread::get_id() << "打印g_v值" << g_v[0] << g_v[1] << g_v[2] << endl;
//
//}



class A
{
public:
	//把玩家命令放入到一个队列的进程
	std::unique_lock<std::mutex>rtn_unique_lock()
	{
		std::unique_lock<std::mutex>temguard(my_mutex1);
		return temguard;
		//从函数返回一个局部的unique_lock对象是可以的,移动构造函数
		//返回这种局部对象temguard会导致系统生成临时unique_lock对象,并调用unique_lock的移动构造函数
	
	}
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; i++)
		{
			//my_mutex.lock();
			
			{


				//my_mutex1.lock();		//先锁金锁 实际中两个lock间会执行其他的东西
				//my_mutex2.lock();		//再锁银锁
				//使用std::lock()
				//std::lock(my_mutex1, my_mutex2);    //相当与每个互斥量都调用了lock
				//std::lock_guard<mutex>sbguard1(my_mutex1, adopt_lock);
				//std::lock_guard<mutex>sbguard2(my_mutex2, adopt_lock);
				//std::unique_lock<std::mutex>sbguard1(my_mutex1);

				//my_mutex1.lock();                     //in线程后到
				//std::unique_lock<std::mutex>sbguard1(my_mutex1,adopt_lock);  //使用了adopt_lock 需要提前lock

				//尝试加锁
				//std::unique_lock<std::mutex>sbguard1(my_mutex1, std::try_to_lock);         //使用此参数的条件时互斥量没有上锁

				//if (sbguard1.owns_lock())              //如果尝试上锁成功
				//{
					

					//创建没有加锁的my_mutex1
					//std::unique_lock<std::mutex>sbguard1(my_mutex1, std::defer_lock);

					//sbguard1.lock();              //不需要手动解锁

					//处理共享代码

					//sbguard1.unlock();              //不需要手动解锁
					//处理非共享代码

					//sbguard1.lock();              //不需要手动解锁
					//处理共享代码
					
					//std::unique_lock<std::mutex>sbguard1(my_mutex1);		// 创建一个加锁的mutex
					//std::unique_lock<std::mutex>sbguard2(std::move(sbguard1)); //将sbguard1的所有权转移给sbguard2
					//现在sbguard1指向空 sbguard2指向了my_mutex1
					/*std::mutex *ptx = sbguard1.release();	*/				//将sbguard1与my_mutex1解绑  手动解锁
					//if (sbguard1.try_lock() == true)
					//{

					std::unique_lock<std::mutex>sbguard1 = rtn_unique_lock();
						cout << "inMsgRecvQueue执行,插入一个元素" << i << endl;
						msgRecvQueue.push_back(i);     //假设数字i为命令 放入队列

						sbguard1.unlock();						//手动解锁
				
					//}
					//else
					//{
					//	cout << "inMsgRecvQueue执行,上锁失败" << endl;
					//	
					//}

					

				//}
				//else								  // 如果上锁失败
				//{
				//	cout << "inMsgRecvQueue 执行,但没有上锁。。。。" << endl;
				//}

					

				//my_mutex2.unlock();				//顺序无所谓
				//my_mutex1.unlock();

			}
		}
	}


	bool outMsgLULproc(int &command)
	{
		//lock_guard<mutex>sbguard(my_mutex1);       //sbguard是对象名
		//my_mutex1.lock();
		//my_mutex2.lock();
		//std::lock(my_mutex1, my_mutex2);

	

		std::unique_lock<std::mutex>sbguard1(my_mutex1);
		std::chrono::milliseconds dura(10);    //等待10ms
		std::this_thread::sleep_for(dura);		  //休息一定时长  outMsgLULproc先跑起来

		if (!msgRecvQueue.empty())
		{
		    command = msgRecvQueue.front();      //返回第一个元素但不检查元素是否存在
			msgRecvQueue.pop_front();            //移除第一个元素但不返回
			//处理数据。。。。。
			/*my_mutex1.unlock();
			my_mutex2.unlock();*/
			return true;
		}	
		else
		{
			//my_mutex1.unlock();
			//my_mutex2.unlock();
			return false; 
		}
	
	}

	//读取命令的线程
	void outMsgRecvQueue()
	{
		int command = 0;	
		for (int i = 0; i < 100000; i++)
		{
			bool result = outMsgLULproc(command);
			if (result == true)
			{
			
				cout << "outMsgRecvQueue 执行,取出一个元素" << command << endl;
				//数据处理
			}

		
		}
		cout << "end" << endl;
	
	}
private:

	std::list<int>msgRecvQueue;
	mutex my_mutex1;           //创建一个互斥量
	mutex my_mutex2;     

};

int main()
{
 //   //一 创建线程和等待多个线程
	//vector<thread>mythreads;
	创建10个线程,线程入口函数统一使用 myprint
	1)多线程执行顺序是乱的
	2)这种join写法更容易写出稳定程序
	3)把thread对象放入到容器里,对管理大量线程有帮助
	//for (int i = 0; i < 10; i++)
	//{
	//	//创建10个线程,已经开始执行
	//	mythreads.push_back(thread(Myprint, i));
	//
	//}
	//for (auto iter = mythreads.begin(); iter != mythreads.end(); ++iter)
	//{
	//	iter->join();
	//}
	//cout << "I LOVE CHINA" << endl;

	//二 数据共享
	//2.1只读数据
	//2.2 有读有写
	//最简单的不崩溃处理 读和写不能同时进行
	//2.3其他案例
	//数据共享:    
	
	//三 共享数据的保护案例代码
	//网络游戏服务器,有两个自己创建的线程,一个线程手机玩家命令(数字表示),并把名利数据写入到一个队列中。
	//另一个线程从队列中取出玩家发送来的命令,解析,然后执行玩家的动作。
	//使用list,频繁的按顺序插入和删除时效率较高


	//用成员函数作为线程函数的方法写线程

	A myobja;
	thread myOutnMsg(&A::outMsgRecvQueue, &myobja);
	thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
	myOutnMsg.join();
	myInMsgObj.join();

	//四 互斥量的概念
	//步骤:先lock 操作共享数据 然后unlock
	//lock和unlock要成对使用。有lock忘记unlock的问题非常难排查
	//为了防止忘记unlock(),引入了一个叫std::lock_guard的类模板
	//智能指针(unique_ptr<>)

	//std::lock_guard类模板 直接取代lock()和unlock(),用了类模板不能再用lock和unlock
	//要将保护量放在lock和unlock里


	//五 死锁
	/*
	**死锁问题的前提条件是:有至少两个锁,即至少两个互斥量,金锁(Jinlock),银锁(Yinlock)**
	两个线程A,B
	线程A执行时,先锁金锁,然后去锁银锁
	两个线程出现了上下文切换,线程B执行了,线程B先锁银锁,因为银锁没有被A锁上,所以被B锁上了,然后线程B去锁金锁
	此时产生了死锁
	线程A锁不了银锁,流程走不下去
	线程B锁不了金锁,流程走不下去
	死锁产生的关键是两个互斥量的上锁顺序不一致
	*/

	//5.1死锁演示
	//5.2死锁的一般解决方案
	//只要保证两个互斥量上锁的顺序一致,就不会造成死锁。


	//5.3 std::lock()函数模板
	//能力:一次锁住两个或者两个以上的互斥量(至少两个,多了不限,一个不行)
	//不存在因为锁头的顺序问题导致出现死锁问题。
	//如果互斥量中有一个没锁住,则等待,等待所有互斥量否锁住,才能继续往下走
	//特点:要么两个互斥量都锁住,要么两个互斥量都没锁住。如果只锁了一个,另外一个没成功,则立即解锁已经锁住的。
	//用来处理多个互斥量的情况

	//5.4 std:lock_guard的std:adopt_lock参数


	//六 unique_lock取代lock_guard
	//unique_lock是个类模板,工作中推荐使用lock_guard
	//lock_guard取代了mutex的lock和unlock
	//unique_lock比lock_guard灵活,效率略差,内存占用稍多。

	//七 unique_lock的第二个参数

	// 7.1 adopt_lock
	//lock_guard中的adopt_lock起标记作用 表示互斥量已经被lock了,在写这个标记前需要把互斥量lock 否则报错
	//adopt的效果是:假设调用方 线程已经拥有了互斥的所有权(已经lock成功了)
	//unique_lock也有adopt_lock标记,含义相同。
	//灵活性:
	//对于同一个锁 如果其中一个线程上了锁,则另一个也需要这个锁的线程就会处于等待状态,知道前一个线程解锁,会浪费很多时间
	

	//7.2 std::try_to_lock
	//尝试用mutex的lock()去锁定lock,但如果没有锁定成功,也会立即返回,并不会阻塞
	//用try_to_lock的前提是不可以手动上锁,否则异常
	

	//7.3 std::defer_lock
	//使用defer_lock的前提是 不能自己先lock,否则异常,与try to lock一样,与adopt_lock不同
	//defer_lock  初始化一个没有加锁的mutex
	//借着defer_lock的话题,介绍一些unique_lock的重要成员函数


	//八 unique_lcok的成员函数

	// 8.1 lock()

	//8.2 unlock()  希望处理些非共享代码,处理非共享代码,然后再lock上
	//即unique_lock通过成员函数可以实现手动上锁,自动解锁和手动解锁
	//也可以自己在程序结尾自己家unlock  unique_lock会作出判断

	//8.3 try_lock 尝试给互斥量加锁,如果拿不到锁,则返回false,拿到了则返回true,程序不阻塞


	//8.4 release(),返回它管理的mutex对象指针,并释放所有权,也就是说,这个unique_lock和mutex不再有关系
	//严格区分unlock()和release()的区别,不要混淆
	//如果原来mutex对象处于加锁状态,如果release了,则需要手动解锁
	//release返回原始的mutex指针
	//为什么有时候需要unlock():因为lock锁住的代码段越少,执行越快,整个程序运行效率越高
	//乘互斥量锁住的代码多少,称为粒度 ,粒度一般用粗细描述
	//要选择合适粒度的代码来保护共享数据,粒度太细影响效率,粒度太粗影响效率
	


	// 九 unique_lock的所有权传递
	// 一个mutex只和一个mutex绑定在一起  
	//所有权概念: sbguard1拥有my_mutex1的所有权,sbguard1可以把对my_mutex1的所有权转一个其他unique_lock对象,
	//所以,unique_lock的所有权,可以转移,不能复制。
	//std::unique_lock<std::mutex>sbguard1(my_mutex1);
	//std::unique_lock<std::mutex>sbguard2(sbguard1);  这样是非法的
	//std::unique_lock<std::mutex>sbguard2(std::move(sbguard1)); //将sbguard1的所有权转移给sbguard2
}


在这里插入图片描述
在这里插入图片描述
可以看到my_mutex1的地址被绑定到了sbguard1上了

标签:std,unique,mutex1,lock,C++,并发,线程,多线程,my
来源: https://blog.csdn.net/dddgggd/article/details/116379515