12 —— 虚假唤醒、线程数量谈、线程池浅谈

一 虚假唤醒

  什么是虚假唤醒?我们先看以前关于condition_variable的例子

class A
{
public:
	// 把收到的消息传入队列
	void inMsgRecvQueue()
	{
		for (size_t i = 0; i < 1000; ++i)
		{
			cout << "收到消息,并放入队列 " << i << endl;

			std::unique_lock<std::mutex> my_uniq(my_mutex);
			msgRecvQueue.push_back(i);
			my_condi.notify_one(); // 我们尝试把my_condi.wait()唤醒,通知它。
		}

		cout << "消息入队结束" << endl;
	}

	// 从队列中取出消息
	void outMsgRecvQueue()
	{
		while (true)
		{
			// 获取my_mutex,并加锁
			std::unique_lock<std::mutex> my_uniq(my_mutex);

			my_condi.wait(my_uniq, [this] {
				if (!msgRecvQueue.empty())
					return true;
				else
					return false;
			});


			int command = msgRecvQueue.front();
			msgRecvQueue.pop_front();


			my_uniq.unlock();
			//	其它业务

			cout << "出队列:" << command << endl;

		}

	}
}

  虚假唤醒就是:当条件变量的wait()函数被唤醒时,它的条件并没有满足(第二个参数,或者说条件表达式返回false),这就是虚假唤醒。
  例如,在上述代码中,如果入队列成功后,将my_condi.notify_one(); 调用多次,并且在出队列的wait()函数中,没有第二个参数(条件表达式),那么就可能存在一种情况,就是队列已经为空,但是出队列的线程仍然会从队列中取数据。再比如,如果有多个取数据的线程,一个入队列的线程。当插入一条数据时,调用notify_all()通知其它线程去取。可能一个线程将数据取走后,其它的线程取的时候,队列已经为空。
  总面言之,就是上述代码中,wait()被唤醒的时候,队列中实际没有数据。 可上面的代码不会发生这样的情况,因为它加了条件表达式进行判断,不会被虚假唤醒。

  避免虚假唤醒:wait()中要有第二参数(lambda)并且能正确的判断公共数据是否存在。

二 线程池

  假设有以下情况:
  A 你在线一个服务端程序,每当一个客户端连接,就必须创建一个新的线程为它服务。
  B 你做一个网络游戏,每当有游戏玩家进行充值时,你必须分配新的线程为其服务。

以上情景会面临这样的问题,如果客户端或者充值的玩家数量有很多,你的系统就分配不出这么多线程资源,那么程序就有可能会奔溃。这时,线程池就能解决这样的问题。线程池就是把一堆线程放在一起统一管理,通过循环利用其中的第一个线程进行任务调度。

线程池的实现方式:在程序一启动时就创建一定数量的线程,这个数量一般能由系统的API提供。

三 线程数量谈

  • 线程开的数量的极限是2000个左右。
  • 创建线程使用某些API的接口建议的个数
  • 创建线程数量根据你需要完成业务的数量。比如,你有100用户同时充值,你可以开110个线程。
  • 自己测试,实践,测试多少线程效率最高。尽量不要超过500个。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值