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上了