【C++11并发与多线程笔记(4)】

unique_lock详解

unique_lock取代lock_guard

unique_lock是个类模板,工作中一般lock_guard足够了(推荐使用),lock_guard取代了mutex的lock和unlock();
unique_lock比lock_guard灵活很多,效率上差一点,内存也占用多一点.

unique_lock的第二个参数

std::adopt_lock

std::lock_guard guard(my_mutex, std::adopt_lock);//adopt_lock标记作用
std::adopt_lock:表示这个互斥量已经被lock()过了,(你必须把互斥量提前lock了,否则会报异常)
std::adopt_lock标记效果就是“假设调用方 线程已经拥有了互斥的所有权(已经lock()成功了);”
通知lock_guard不需要在构造函数中lock()这个互斥量了。
unique_lock也可以带std::adopt_lock标记,含义相同,就是不希望在unique_lock构造函数中lock中lock这个mutex;

class A
{
public:

	//把收到消息(玩家命令)入到一个队列的线程
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			my_mutex.lock();
			std::unique_lock<std::mutex> guard(my_mutex,std::adopt_lock);
			msgRecvQueue.push_back(i);
		}
	}
	bool outMsgLULProc(int& command) {
		my_mutex.lock();
		std::unique_lock<std::mutex> guard(my_mutex, std::adopt_lock);
		if (!msgRecvQueue.empty()) {
			command = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			return true;
		}
		return false;
	}
	void outMsgRecvQueue() {
		for (int i = 0; i < 100000; i++) {
			int command = 0;
			bool result = outMsgLULProc(command);
			if (result) {
				cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl;

			}
			else {
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
			}
		}
		cout << "end" << endl;
	}

private:
	std::list<int> msgRecvQueue;//容器,专门用于代表玩家发过来的命令。
	std::mutex my_mutex;//创建了一个互斥量(一个互斥量就是一把锁)
};
//main:
A a;
thread outThread(&A::outMsgRecvQueue, &a);
thread inThread(&A::inMsgRecvQueue, &a);
outThread.join();
inThread.join();

std::try_to_lock

我们会尝试用mutex的lock()去锁定这个mutex,但如果没有锁定成功,我也会立即返回,并不会阻塞在那里;
用这个try_to_lock的前提是你自己不能先去lock

class A
{
	//把收到消息(玩家命令)入到一个队列的线程
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			cout << "inMsgRecvQueue执行 插入一个元素:" << i << endl;

			std::unique_lock<std::mutex> guard(my_mutex,std::try_to_lock);
			if (guard.owns_lock()) {
				//拿到了锁
				msgRecvQueue.push_back(i);
			}
			else {
				//没拿到锁,析构的时候自然不会,unlock()
				cout << "inMsgRecvQueue()执行,但没有拿到锁,只能干点别的事" << i << endl;
			}
		}
	}
	bool outMsgLULProc(int& command) {
		my_mutex.lock();
		std::chrono::milliseconds dura(200000);//1秒=1000毫秒
		std::this_thread::sleep_for(dura);//休息20秒
		std::unique_lock<std::mutex> guard(my_mutex, std::adopt_lock);
		if (!msgRecvQueue.empty()) {
			command = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			return true;
		}
		return false;
	}
	void outMsgRecvQueue() {
		for (int i = 0; i < 100000; i++) {
			int command = 0;
			bool result = outMsgLULProc(command);
			if (result) {
				cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl;

			}
			else {
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
			}
		}
		cout << "end" << endl;
	}

private:
	std::list<int> msgRecvQueue;//容器,专门用于代表玩家发过来的命令。
	std::mutex my_mutex;//创建了一个互斥量(一个互斥量就是一把锁)
};
//main:
A a;
thread outThread(&A::outMsgRecvQueue, &a);
thread inThread(&A::inMsgRecvQueue, &a);
outThread.join();
inThread.join();

std::defer_lock

用这个defer_lock的前提是,你不能自己先lock,否则会报异常
defer_lock的意思就是并没有给mutex加锁:初始化了一个没有加锁的mutex.
unique_lock成员函数,
3.1 lock()
3.2 unlock();
3.3 try_lock();
3.4 release()

unique_lock的成员函数

lock()

unlock

class A
{
public:
	//把收到消息(玩家命令)入到一个队列的线程
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			cout << "inMsgRecvQueue执行 插入一个元素:" << i << endl;
			std::unique_lock<std::mutex> guard(my_mutex, std::defer_lock); //创建没有加锁的my_mutex
			guard.lock();//咱们不用自己unlock();
			//因为有一些非共享代码要处理,先把锁解开,处理非共享代码
			guard.unlock();
			guard.lock();
			msgRecvQueue.push_back(i);
			guard.unlock();//画蛇添足,但是也没错
			//my_mutex.lock();
			//std::unique_lock<std::mutex> guard(my_mutex,std::try_to_lock);
			//if (guard.owns_lock()) {
			//	//拿到了锁
			//	msgRecvQueue.push_back(i);
			//}
			//else {
			//	cout << "inMsgRecvQueue()执行,但没有拿到锁,只能干点别的事" << i << endl;
			//}
			//msgRecvQueue.push_back(i);

			//if (guard.try_lock()) {
				msgRecvQueue.push_back(i);
			//}
			//else {
			//	cout << "inMsgRecvQueue()执行,但没有拿到锁,只能干点别的事" << i << endl;
			//}
			ptr->unlock();

		}
	}
	bool outMsgLULProc(int& command) {
		my_mutex.lock();
		std::unique_lock<std::mutex> guard(my_mutex, std::adopt_lock);//lock_guard构造函数里执行了mutex::lock()
		if (!msgRecvQueue.empty()) {
			command = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			return true;
		}
		return false;
	}
	void outMsgRecvQueue() {
		for (int i = 0; i < 100000; i++) {
			int command = 0;
			bool result = outMsgLULProc(command);
			if (result) {
				cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl;

			}
			else {
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
			}
		}
		cout << "end" << endl;
	}

private:
	std::list<int> msgRecvQueue;//容器,专门用于代表玩家发过来的命令。
	std::mutex my_mutex;//创建了一个互斥量(一个互斥量就是一把锁)
};
//main:
A a;
thread outThread(&A::outMsgRecvQueue, &a);
thread inThread(&A::inMsgRecvQueue, &a);
outThread.join();
inThread.join();

try_lock

class A
{
public:
	//把收到消息(玩家命令)入到一个队列的线程
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			cout << "inMsgRecvQueue执行 插入一个元素:" << i << endl;
			std::unique_lock<std::mutex> guard(my_mutex, std::defer_lock); //创建没有加锁的my_mutex
			if (guard.try_lock()) {
				msgRecvQueue.push_back(i);
			}
			else {
				cout << "inMsgRecvQueue()执行,但没有拿到锁,只能干点别的事" << i << endl;
			}


		}
	}
	bool outMsgLULProc(int& command) {
		my_mutex.lock();
		std::chrono::milliseconds dura(200000);//1秒=1000毫秒
		std::this_thread::sleep_for(dura);//休息20秒
		std::unique_lock<std::mutex> guard(my_mutex, std::adopt_lock);//lock_guard构造函数里执行了mutex::lock()
		if (!msgRecvQueue.empty()) {
			command = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			return true;
		}
		return false;
	}
	void outMsgRecvQueue() {
		for (int i = 0; i < 100000; i++) {
			int command = 0;
			bool result = outMsgLULProc(command);
			if (result) {
				cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl;

			}
			else {
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
			}
		}
		cout << "end" << endl;
	}

private:
	std::list<int> msgRecvQueue;//容器,专门用于代表玩家发过来的命令。
	std::mutex my_mutex;//创建了一个互斥量(一个互斥量就是一把锁)
};
//main:
A a;
thread outThread(&A::outMsgRecvQueue, &a);
thread inThread(&A::inMsgRecvQueue, &a);
outThread.join();
inThread.join();

release()

release()返回它所管理的mutex对象指针,并释放所有权,也就是说,这个unique_lock和mutex不再有关系
严格区分unlock()和release()的区别,不要混淆。
如果原来这个mutex对象处于加锁状态,你有义务接管过来并且解锁。返回的是原始的mutex指针
为什么有时候需要unlock(),因为你lock锁住的代码段越少,执行越快,整个程序运行效率越高。
有人也把锁头锁住代码的多少称为锁的粒度,粒度一般用粗细来描述
a)锁住的代码多少,这个粒度叫细,执行效率。
b)锁住的代码多,粒度叫粗,那执行效率就低;
要学会尽量选择合适粒度的diamagnetic进行保护,粒度太细,可能漏掉共享的数据保护,粒度太粗,影响效率。
选择合适的粒度,是高级程序员的能力和实力的体现

class A
{
public:
	//把收到消息(玩家命令)入到一个队列的线程
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			cout << "inMsgRecvQueue执行 插入一个元素:" << i << endl;
			std::unique_lock<std::mutex> guard(my_mutex);
			std::mutex* ptr = guard.release();
			ptr->unlock();
		}
	}
	bool outMsgLULProc(int& command) {
		my_mutex.lock();
		std::unique_lock<std::mutex> guard(my_mutex, std::adopt_lock);//lock_guard构造函数里执行了mutex::lock()
		if (!msgRecvQueue.empty()) {
			command = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			return true;
		}
		return false;
	}
	void outMsgRecvQueue() {
		for (int i = 0; i < 100000; i++) {
			int command = 0;
			bool result = outMsgLULProc(command);
			if (result) {
				cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl;

			}
			else {
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
			}
		}
		cout << "end" << endl;
	}

private:
	std::list<int> msgRecvQueue;//容器,专门用于代表玩家发过来的命令。
	std::mutex my_mutex;//创建了一个互斥量(一个互斥量就是一把锁)
};
//main:
A a;
thread outThread(&A::outMsgRecvQueue, &a);
thread inThread(&A::inMsgRecvQueue, &a);
outThread.join();
inThread.join();

unique_lock所有权的传递

std::unique_lockstd::mutex guard(my_mutex);所有权概念
guard拥有my_mutex的所有权,
guard可以把自己对(my_mutex)的所有权转移给其它的unique_lock对象;
所以,unique_lock对象这个mutex的所有权是属于可以转移,但是不能复制。跟5章6、7节讲智能指针unique_ptr类似。

class A
{
public:
	std::unique_lock<std::mutex> rtn_unique_lock() {//所有权转移方式二:
		std::unique_lock<std::mutex> tempGuard(my_mutex);//从函数返回一个局部的unique_lock对象是可以的,三章十四节讲过移动构造函数。
														//	返回这种局部对象tempGuard会导致系统生成临时unique_lock对象,并调用unique_lock的移动构造函数。
		return tempGuard;
	};
	//把收到消息(玩家命令)入到一个队列的线程
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			cout << "inMsgRecvQueue执行 插入一个元素:" << i << endl;
			std::unique_lock<std::mutex> guard(my_mutex);
			std::unique_lock<std::mutex> guard1(std::move(guard));//所有权转移方式一:移动语义,现在相当于guard1和my_mutex绑定到一起了
																	//现在guard指向空
			//std::unique_lock<std::mutex> guard = rtn_unique_lock();
			std::mutex* ptr = guard1.release();
			msgRecvQueue.push_back(i);
			ptr->unlock();

		}
	}
	bool outMsgLULProc(int& command) {
		my_mutex.lock();
		std::unique_lock<std::mutex> guard(my_mutex, std::adopt_lock);
		if (!msgRecvQueue.empty()) {
			command = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			return true;
		}
		return false;
	}
	void outMsgRecvQueue() {
		for (int i = 0; i < 100000; i++) {
			int command = 0;
			bool result = outMsgLULProc(command);
			if (result) {
				cout << "outMsgRecvQueue()执行,取出一个元素" << command << endl;

			}
			else {
				cout << "outMsgRecvQueue()执行,但目前消息队列中为空" << i << endl;
			}
		}
		cout << "end" << endl;
	}

private:
	std::list<int> msgRecvQueue;//容器,专门用于代表玩家发过来的命令。
	std::mutex my_mutex;//创建了一个互斥量(一个互斥量就是一把锁)
};
A a;
thread outThread(&A::outMsgRecvQueue, &a);
thread inThread(&A::inMsgRecvQueue, &a);
outThread.join();
inThread.join();

所有权转移方式有两种;

单例设计模式共享数据分析、解决,call_once

设计模式大概谈

“设计模式”:代码的一些写法(这些写法跟常规写法不太一样):程序灵活,维护起来可能方便,但是别人接管和阅读代码都会很痛苦
用“设计模式”理念写出来的代码很晦涩;《head first》
老外应付特别大的项目的时候,把项目开发经验,模块划分经验,总结整理成设计模式(先有开发需求,后有理论总结和整理);
设计模式拿到中国来,不太一样,拿着一个程序(项目)往设计模式上套,一个小小的项目,它非要弄成几个设计模式进去,本末倒置。
设计模式肯定有它独特的优点,要活学活用,不要深限其中,生搬硬套。

单例设计模式

单例设计模式,使用的频率比较高;
单例:整个项目中,由某个或者某些特殊的类,属于该类的对象,我只能创建一个,多了我创建不了

class MyCAS//这是一个单例类
{
private:
	MyCAS() {};//私有化了构造函数
private:
	static MyCAS* m_instance;//静态成员变量
public:
	static MyCAS* GetInstance() {
		 if(m_instance ==NULL){
		 //两个进程可能同时到达这里,然后创建了两个指针,这时候违背了单例类的原则
		 	m_instance = new MyCAS();
		 }
		 return m_instance;
	}
	void func()
	{
		cout << "测试" << endl;
	};
	class CGarhuishou {//类中套类,用来释放对象
	public:
		~CGarhuishou()
		{
			if (MyCAS::m_instance) {
				delete MyCAS::m_instance;
				MyCAS::m_instance = NULL;
			}
		}
	};
};
MyCAS* MyCAS::m_instance = NULL;

void mythread() {
	cout << "我的线程开始执行了" << endl;
	MyCAS* p_a = MyCAS::GetInstance();//这里可能就有问题了,虽然这两个线程是同一个入口函数,但大家千万记住,这是两个线程,所以这里会有两个流程开始执行mythread函数,
	cout << "我的线程执行完毕了" << endl;

}
std::thread myobj1(mythread);
std::thread myobj2(mythread);
myobj1.join();
myobj2.join();

MyCAS* p_a = MyCAS::GetInstance();//返回该类对象的指针
p_a->func();
MyCAS::GetInstance()->func();//在这里对该转载的数据进行装载

单例设计模式共享数据问题分析、解决

面临的问题:需要我们在我们创建的线程(而不是主线程)中来创建MyCAS这个单例类的对象,这种这种线程可能不止一个;
我们可能会面临GetInstance()这种成员函数要互斥;

std::mutex resource_mutex;
class MyCAS//这是一个单例类
{
	static void createInstance() {
		m_instance = new MyCAS();
		static CGarhuishou cl;
	}
private:
	MyCAS() {};//私有化了构造函数
private:
	static MyCAS* m_instance;//静态成员变量
public:
	static MyCAS* GetInstance() {
		if (m_instance == NULL) {//双重锁定(双重检查)
			std::unique_lock<std::mutex> mymutex(resource_mutex);
			if (m_instance == NULL) {
				m_instance = new MyCAS();
				static CGarhuishou cl;
			}
		}
		return m_instance;

	}
	void func()
	{
		cout << "测试" << endl;
	};
	class CGarhuishou {//类中套类,用来释放对象
	public:
		~CGarhuishou()
		{
			if (MyCAS::m_instance) {
				delete MyCAS::m_instance;
				MyCAS::m_instance = NULL;
			}
		}
	};
};
MyCAS* MyCAS::m_instance = NULL;

void mythread() {
	cout << "我的线程开始执行了" << endl;
	MyCAS* p_a = MyCAS::GetInstance();//这里可能就有问题了
	cout << "我的线程执行完毕了" << endl;

}
//main
std::thread myobj1(mythread);
std::thread myobj2(mythread);
myobj1.join();
myobj2.join();

std::call_once()

std::call_once() c++引入的函数,该函数的第二个参数是一个函数名;
call_once功能就是保证函数只能被调用一次
call_once具备互斥量这种能力,而且效率上,比互斥量消耗的资源更少
call_once()需要与一个标记结合使用,这个标记是std::once_flag;其实once_flag是一个结构;
call_once()就是通过这个标记来决定对应的函数a()是否执行,调用call_once()成功后,call_once()就把这个标记设置为一种一种已调用的状态
后续再次调用call_once(),只要once_flag被设置为了“已调用”状态,那么对应的函数a()就不会再被执行了


std::once_flag g_flag;//这是个系统定义的标记
class MyCAS//这是一个单例类
{
	static void createInstance() {
		m_instance = new MyCAS();
		static CGarhuishou cl;
	}
private:
	MyCAS() {};//私有化了构造函数
private:
	static MyCAS* m_instance;//静态成员变量
public:
	static MyCAS* GetInstance() {

		call_once(g_flag, createInstance);//两个线程同时执行到这里,其中一个线程要等到另外一个线程执行完毕createInstance;
		return m_instance;
	}
	void func()
	{
		cout << "测试" << endl;
	};
	class CGarhuishou {//类中套类,用来释放对象
public:
		~CGarhuishou()
		{
			if (MyCAS::m_instance) {
				delete MyCAS::m_instance;
				MyCAS::m_instance = NULL;
			}
		}
	};
};
MyCAS* MyCAS::m_instance = NULL;

void mythread() {
	cout << "我的线程开始执行了" << endl;
	MyCAS* p_a = MyCAS::GetInstance();//这里可能就有问题了
	cout << "我的线程执行完毕了" << endl;

}
std::thread myobj1(mythread);
std::thread myobj2(mythread);
myobj1.join();
myobj2.join();
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值