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

windows临界区

多次进入临界区试验

在“同一个线程”(不同线程就会卡住等待)中,windows中的“相同临界区变量”,代表的临界区的进入(EnterCriticalSection(&my_winsec))可以被多次调用
但是你调用了几次EnterCriticalSection(&my_winsec),你就得多次调用LeaveCriticalSection(&my_winsec);
而在c++11中不允许lock同一个互斥量多次,否则一样

自动析构技术

#define __WINDOWSJQ_ 11
class CWclock {//本类用于自动释放windows下的临界区,防止忘记LeaveCriticalSection导致死锁的情况发生,类似于c++11中的std::lock_guard<std::mutex>
public://叫做RAII类(Resource Acquisition is initialization)中文“资源获取即初始化”
				//容器,智能指针,都属于RAII类
	CWclock(CRITICAL_SECTION* pCritical) {
		m_pCritical = pCritical;
		EnterCriticalSection(m_pCritical);
	}
	~CWclock() {
		LeaveCriticalSection(m_pCritical);
	}

private:
	CRITICAL_SECTION* m_pCritical;
};


class A
{
public:
	//把收到消息(玩家命令)入到一个队列的线程
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			cout << "inMsgRecvQueue执行 插入一个元素:" << i << endl;
#ifdef __WINDOWSJQ_
			CWclock clock(&my_winsec);
			CWclock clock1(&my_winsec);
			//EnterCriticalSection(&my_winsec);//进入临界区(加锁)
			//EnterCriticalSection(&my_winsec);
			msgRecvQueue.push_back(i);
			//LeaveCriticalSection(&my_winsec);//离开临界区(解锁)
			//LeaveCriticalSection(&my_winsec);
#else
			std::lock_guard<std::mutex> guard(my_mutex);			
			msgRecvQueue.push_back(i);

#endif
		}
	}
	bool outMsgLULProc(int& command) {

#ifdef __WINDOWSJQ_
		EnterCriticalSection(&my_winsec);
		if (!msgRecvQueue.empty()) {
			int command = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			LeaveCriticalSection(&my_winsec);
			return true;
		}
		LeaveCriticalSection(&my_winsec);
#else
		std::lock_guard<std::mutex> guard(my_mutex);

		if (!msgRecvQueue.empty()) {
			int command = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			return true;
		}

#endif
		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;
	}
	A() {
		#ifdef __WINDOWSJQ_
			InitializeCriticalSection(&my_winsec);//用临界区之前要先初始化
		#endif
	}

private:
	std::list<int> msgRecvQueue;//容器,专门用于代表玩家发过来的命令。
#ifdef __WINDOWSJQ_
	CRITICAL_SECTION my_winsec;//windows中的临界区,非常类似于c++11中的mutex
#endif
};
//main
A a;
thread outThread(&A::outMsgRecvQueue, &a);
thread inThread(&A::inMsgRecvQueue, &a);
outThread.join();
inThread.join();
cout << "I love china!" << endl;

recursive_mutex递归的独占互斥量

recursive_mutex递归的独占互斥量
std::mutex:独占互斥量,自己lock时别人lock不了
std::recursive_mutex:递归的独占互斥量:允许同一个线程,同一个互斥量多次被.lock();效率上比mutex要差一点
recursive_mutex有lock也有unlock
考虑代码是否有优化空间
递归次数据说有限制,递归次数太多可能会异常

class A
{
public:
	//把收到消息(玩家命令)入到一个队列的线程
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
			cout << "inMsgRecvQueue执行 插入一个元素:" << i << endl;
			std::lock_guard<std::recursive_mutex> guard(my_mutex);
			testFuncion1();//加了三次锁报异常(只要lock超过一次就会报异常)
			msgRecvQueue.push_back(i);
		}
	}
	bool outMsgLULProc(int& command) {
		std::lock_guard<std::mutex> guard(my_mutex);
		if (!msgRecvQueue.empty()) {
			int 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;
	}
	
void testFuncion1() {
	std::lock_guard<std::recursive_mutex> guard1(my_mutex);
	//干各种事情
	testFuncion2();//某些原因testFunction1要调用testFunction2
}
void testFuncion2() {
	std::lock_guard<std::recursive_mutex> guard2(my_mutex);

}
private:
	std::list<int> msgRecvQueue;//容器,专门用于代表玩家发过来的命令。
	std::recursive_mutex my_mutex;//独占递归互斥量
};
//main
A a;
thread outThread(&A::outMsgRecvQueue, &a);
thread inThread(&A::inMsgRecvQueue, &a);
outThread.join();
inThread.join();
cout << "I love china!" << endl;

带超时的互斥量std::timed_mutex和std::recursive_time_mutex

带超时的互斥量std::timed_mutex和std::recursive_timed_mutex
std::timed_mutex:是带超时功能的独占互斥量;
try_lock_for:参数是等待一段时间。如果我拿到了锁,或者等待超过时间没拿到锁,就走下来
try_lock_until() :参数是一个未来的时间点,在这个未来的时间没到的时间内,如果拿到了锁,那么就走下来
如果时间到了,没拿到锁,流程也走下来
std::recursive_time_mutex:带超时功能的递归互斥量,允许用一个线程多次获取这个互斥量

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

			std::lock_guard<std::timed_mutex> guard(my_mutex);
			std::chrono::milliseconds timeout(100);//100毫秒
			/*if (my_mutex.try_lock_for(timeout)) {*/
			if (my_mutex.try_lock_until(chrono::steady_clock::now()+timeout)) {
				//在这100毫秒之内拿到了锁
				msgRecvQueue.push_back(i);
				my_mutex.unlock();
			}
			else {
				//这次没拿到锁
				std::chrono::milliseconds sleeptime(100);
				std::this_thread::sleep_for(sleeptime);
			}
		}
	}
	bool outMsgLULProc(int& command) {
		std::lock_guard<std::timed_mutex> guard(my_mutex);
		if (!msgRecvQueue.empty()) {
			int command = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			my_mutex.unlock();//程序有两个出口,需要两个unlock();
			return true;
		}
		my_mutex.unlock();//必须使用同一个互斥量加锁解锁
		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::timed_mutex my_mutex;
};

补充知识、线程池浅谈、数量谈、总结

补充一些知识点

虚假唤醒

class A
{
public:
	

	//把收到消息(玩家命令)入到一个队列的线程
	void inMsgRecvQueue() {
		for (int i = 0; i < 10000; i++) {
		cout << "inMsgRecvQueue()执行,插入一个元素!" << endl;
		my_mutex.lock();
		std::unique_lock<std::mutex> guard(my_mutex, std::adopt_lock);
		msgRecvQueue.push_back(i);
		//假如outMsgRecvQueue()正在处理一个事务,需要一段时间,而不是正卡在wait()那里等待你唤醒,那么此时这个notify_one没有效果
		my_cond.notify_one();//我们尝试把wait()的线程唤醒,执行完这行,那么outMsgRecvQueue()里面的wait就会被唤醒
		}
	}
	void outMsgRecvQueue() {
		int command = 0;
		while (true) {
		std::unique_lock<std::mutex> guard(my_mutex);
		my_cond.wait(guard, [this] {//wait()用来等一个东西,如果第二个参数返回值为true,那么wait()直接返回,执行结束。
		//如果第二个参数lambda表达式返回值是false,那么wait()将解锁互斥量,并堵塞到本行
		//那堵塞到什么时候为止呢?堵塞到其它线程调用notify_one()成员函数为止;
		//如果wait()没有第二个参数:my_cond.wait(guard),就跟第二个参数lambda表达式返回false效果一样
		//wait()将解锁互斥量,并堵塞到本行,堵塞到其它某个线程调用notify_one()成员函数为止;
		if (!msgRecvQueue.empty()) {
			return true;
		}
		return false;
		});
	//当其它线程用notify_one将本wait(原本是堵塞状态)唤醒后,wait就开始干活了,恢复后干什么活?
	//a)wait()不断的尝试重新获取互斥量所,如果获取不到,那么流程就卡在wait这里等着获取,如果获取到了,那么wait()就继续执行b;
	//b)
			//b.1)如果wait有第二个参数(lambda),就判断这个lambda表达式,如果lambda表达式为false,那么wait()又将解锁互斥量,并堵塞到本行
			//b.2)如果lambda表达式为true,则wait()返回,流程走下来(此时互斥锁被锁下来),
				//b.3)如果wait()没有第二个参数,则wait返回,流程走下来。
	//流程只要能走到这里来,这个互斥锁一定是锁着的,同时msgRecvQueue至少是有一条数据的。
	command = msgRecvQueue.front();
	msgRecvQueue.pop_front();
	cout << "outMsgRecvQueue()执行,取出一个元素" << command << "thread_id:" << std::this_thread::get_id() << endl;

	guard.unlock();//因为unique_lock的灵活性,所以我们可以随时unlock解锁,以免锁住太长时间
	}
		
	}
private:
	std::list<int> msgRecvQueue;//容器,专门用于代表玩家发过来的命令。
	std::mutex my_mutex;//创建了一个互斥量(一个互斥量就是一把锁)
	std::condition_variable my_cond;//生成一个条件变量对象
};
//main
A a;
	thread outThread(&A::outMsgRecvQueue, &a);
	thread inThread(&A::inMsgRecvQueue, &a);
	outThread.join();
	inThread.join();

atomic

class A
{
public:
	std::atomic<int> atm;
	A() {
		atm = 0;
		//auto atm2 = atm;//这种定义时初始化不允许,显示“尝试引用已删除的函数”编译器内部肯定把拷贝构造函数干掉了
		//atd::atomic<int> atm2;
		//atm2=atm;//尝试引用已经删除的函数,拷贝复制运算也不让用
		//load():以原子方式读atomic对象的值;
		atomic<int> atm2(atm.load());
		auto atm3(atm.load());
		//store()以原子方式写入内容
		atm2.store(3);
	}
}

浅谈线程池

场景设想

实现方式

浅谈线程池
(2.1)场景设想
开发一个服务器程序-》》客户端,每来一个客户端请求,就创建一个新线程为该客户提供服务。
a)网络游戏,2万个玩家不可能给每个玩家创建个新线程,此程序写法在这种场景下不通
b)程序稳定性问题:编写代码中,偶尔创建一个线程,这种代码写法,就让人感到不安,
线程池:把一堆线程弄到一起,统一管理。这种统一管理调度,循环利用线程的方式,就叫线程池
(2.2)实现方式
在程序启动的时候,我一次性创建好一定数量的线程(比如10个),更让人感到放心,觉得程序代码更稳定。

线程创建数量谈

3.1)线程开的数量有限问题,2000个线程基本就是极限,再创建线程就崩溃
3.2)线程创建的数量建议:
a)采用某些技术开发程序;接口提供商建议你创建线程数量=cpu数量,cpu2,cpu4,遵照专业的建议和指示来,确保程序高效执行
b)创建多线程完成业务,一个线程等于一条执行通路,100要堵塞充值,我们这里开110个线程,那是很合适的

c++11多线程总结

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值