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

一:补充一些知识点

<1>虚假唤醒

class MA
{
public:
	int i = 0;
	std::unique_lock<std::mutex> rtn_unique_gurad()
	{
		std::unique_lock <std::mutex> tmpgurad(my_mutex_1);
		return tmpgurad;  //从函数返回一个局部的unique_lock对象是可以的,三章十四届讲过移动构造函数
		//返回这种局部对象会导致系统生成临时unique_lock对象,并调用unique_lock的移动构造函数
	}

	//把收到的消息(玩家命令)放入到一个队列的线程
	void inMsgRecvQueue()
	{
		for (int i = 0; i < 100000; ++i)
		{
			cout << "inMsgRecvQueue()执行,插入一个元素:  " << i << endl;
			std::unique_lock<std::mutex> sbguard_1 = rtn_unique_gurad();
			msgRecvQueue.push_back(i);  //假设这个数字i就是我们收到的命令,直接弄到消息队列中

			//假如outMsgRecvQueue()正在处理一个事务,需要一段时间,而不是正卡在wait()那里等待唤醒;
			//那么此时这个notify_one()这个调用也许就没效果。
			my_cond.notify_one();  //我们尝试把wait()线程唤醒,执行完这行,那么outMsgRecvQueue()里边的wait()就会被唤醒
		}
		cout << "i = " << i << endl;
	}

	//把数据从消息队列中取出的线程
	void outMsgRecvQueue()
	{
		int command = 0;
		while (true)
		{
			//wait()用来等待一个东西
			//如果第二个参数lambda表达式返回值是true,那wait()直接返回;
			//如果第二个参数lambda表达式返回值是false,那么wait()将解锁互斥量,并堵塞到本行
			//那堵塞到什么时候为止呢?堵塞到其他某个线程调用notify_one()成员函数为止;
			//如果wait()没有第二个参数:my_cond.wait(sbguard1); 那么就跟第二个参数lambda表达式返回false效果一样,堵塞本行
			//wait()将解锁互斥量,并阻塞到本行,阻塞到其他某个线程调用欧冠notify_one()成员函数为止;

			//当其他线程用notify_one()将本wait()(原来是睡着/阻塞)的状态唤醒后,wait就开始恢复干活了,恢复后wait干什么活?
			//a> wait()不断的尝试重新获取互斥量锁,如果获取不到,那么流程就卡在wait()这这里等着获取,如果获取到了锁(等于加了锁),那么wait()就继续执行b;
			//b> 
			//b.1> 如果wait()有第二个参数(lambda),就判断这个lambda表达式,如果lambda表达式为false,那wait()又对互斥量解锁,然后又休眠这里等待再次被notify_one唤醒;
			//b.2> 如果lambda表达式为true, 则wait返回,流程走下来(此时互斥锁被锁着);
			//b.3> 如果wait()没有第二个参数,则wait()返回,流程走下来。
			std::unique_lock<std::mutex> sbguard_1(my_mutex_1);
			my_cond.wait(sbguard_1, [this] {  //一个lambda就是一个可调用对象(函数)
				if (!msgRecvQueue.empty())
					return true;
				return false;
				});

			//流程只要能走到这里来,这个互斥锁一定是锁着的,同时msgRecvQueue至少是有一条数据的
			command = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			sbguard_1.unlock();  //因为unique_lock的灵活性,所以我们可以随时的unlock解锁,以免锁住太长时间。
			cout << "outMsgRecvQueue()执行,取出一个元素 " << command << " " << std::this_thread::get_id() << endl;
		}  //end while
		cout << "end" << endl;
	}
private:
	list<int> msgRecvQueue;  //容器(消息队列),专门用于代表玩家发送的命令
	mutex my_mutex_1;  //创建了一个互斥量  (一把锁头)
	std::condition_variable my_cond;  //生成一个条件变量对象
};

int main()
{
	MA myobj;
	std::thread myOutMsgObj(&MA::outMsgRecvQueue, &myobj);  //第二个参数是引用,才能保证线程里用的是同一个对象
	std::thread myInMsgObj(&MA::inMsgRecvQueue, &myobj);
	myOutMsgObj.join();
	myInMsgObj.join();
	cout << "main主函数执行结束" << endl;  //最后执行这句,整个进程退出
	return 0;
}

虚假唤醒:wait中要有第二个参数(lambda)并且这个lambda中要正确判断要处理的公共数据是否存在;
wait()、notify_one()、notify_all()。

<2>atomic

class MA
{
public:
	MA()
	{
		atm = 0;
	}

public:
	void inMsgRecv()
	{
		for (int i = 0; i < 10000000; ++i)
		{
			//atm += 1;
			atm = atm + 1;  //读atm是个原子操作,但是整个这一行代码并不是个原子操作
			//auto atm2 = atm;  //这种定义时初始化操作不允许,显示“尝试引用已删除的函数”编译器内部肯定把拷贝构造函数给干掉了用 =delete,因为编译器不好操作
			//atomic<int> atm2 = atm;  //也不允许
			//atomic<int> atm2;
			//atm2 = atm  //尝试引用已删除的函数,拷贝赋值运算符也不让用
			//load():以原子方式读atomic对象的值
			atomic<int> atm2(atm.load());  //读
			auto atm3(atm.load());
			//store()以原子方式写入内容
			atm2.store(12);
			atm2 = 12;
		}
	}

	void outMsgRecv()
	{
		int command = 0;
		while (true)
		{
			cout << "atm = " << atm << endl;
		}  //end while
		cout << "end" << endl;
	}
private:
	atomic<int> atm;
};

int main()
{
	MA myobj;
	std::thread myOutMsgObj(&MA::outMsgRecv, &myobj);  //第二个参数是引用,才能保证线程里用的是同一个对象
	std::thread myInMsgObj(&MA::inMsgRecv, &myobj);
	std::thread myInMsgObj2(&MA::inMsgRecv, &myobj);
	myOutMsgObj.join();
	myInMsgObj.join();
	myInMsgObj2.join();
	cout << "main主函数执行结束" << endl;  //最后执行这句,整个进程退出
	return 0;
}

二:浅谈线程池

<1>场景设想

服务器程序 -> 客户端,每来一个客户端,就创建一个新线程为该客户提供服务。
(1)网络游戏,2万玩家不可能给每个玩家创建个新线程,此程序写法在这种场景下不通;
(2)程序稳定性问题:编写的代码中,偶尔创建一个线程这种代码,这种写法,就让人感到不安;

线程池:把一堆线程弄到一起,统一管理。这种统一管理调试,循环利用线程方式,就叫线程池。

<2>实现方式:
在程序启动时,一次性的创建好一定数量的线程。10,8,100-200,更让人放心,觉得程序代码更稳定。

三:线程创建数量谈

<1>线程开的数量极限问题,2000个线程基本就是极限,再创建线程就崩溃。

<2>线程创建数量建议
(1)采用某些技术开发程序;api接口提供商建议你创建线程数量 = CPU数量、CPU * 2、CPU * 2 + 2,遵照专业建议和指示来;专业意见确保程序高效执行;
(2)创建多线程完成业务;一个线程等于一条通路;100要堵塞充值,我们这里开110个线程,那是很合适的;
(3)1800个线程,建议线程数量尽量不要超过500个,能控制在200以内。

四:c++11多线程总结

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值