《1》补充知识
1.1》虚假唤醒问题
notify_one或者notify_all唤醒wait()后,实际有些线程可能不满足唤醒的条件,就会造成虚假唤醒,可以在wait中再次进行判断解决虚假唤醒。
问题解决:wait中一定要有第二个参数(谓词),并且这个谓词(我们一般都用Lambda表达式来写)中要正确判断所处理的公共数据是否存在!多多细心测试了再使用某段codes,才能让你自己写出安全稳定的多线程codes!
1.2》atomic
std::atomic这个结构体绝不允许对象之间做拷贝copy构造以及operator=拷贝赋值的操作!
因为其源码中含有对这2个函数的=delete的实现:
atomic(const atomic&) = delete;//无法创建拷贝构造函数也无法使用拷贝构造函数来do事情
atomic& operator=(const atomic&) = delete;//无法创建拷贝=赋值函数也无法使用其来do事情
//so
std::atomic<int> atm1 = 1;
std::atomic<int> atm2(atm1);//❌!无法进行copy构造的操作
std::atomic<int> atm3 = atm1;//❌!无法进行operator=的操作
当然,你若是很想要让原子之间do赋值操作,可以调用其.load()成员函数。
.load()成员函数:会以原子的方式来取得atomic对象的值!
//此时,可用.load()函数来do拷贝copy构造以及赋值操作了!
std::atomic<int> atm1 = 1;
std::atomic<int> atm2 = atm1.load();//√!
std::atomic<int> atm3(atm1.load());//√!
当然,有读就有写的操作。
.store()成员函数:会以原子的方式来写入数值给atomic对象!
std::atomic<int> atm1 = 1;
std::atomic<int> atm2 = atm1.load();//√!
atm2.store(12);//atm2 == 12
std::atomic<int> atm3(atm1.load());//√!
atm3.store(13);//atm3 == 13
//当然,你也可以这么干
std::atomic<int> atm1 = 1;
std::atomic<int> atm2 = atm1.load();//√!
atm2 = 12;//atm2 == 12
std::atomic<int> atm3(atm1.load());//√!
atm3 = 13;//atm3 == 13
《2》浅谈线程池(threadPool)
2.1》场景设想
对于一个服务器程序,--》客户端,每来一个客户端,就创建 一个新线程为该客户提供服务
a)网络游戏,2万个玩家不可能给每个玩家都创建一个新的线程对吧?这样你再强大的电脑系统资源也会枯竭。此程序写法在这种场景下不通!
b)程序稳定性问题:if你编写的代码中,有偶尔某个地方创建一个新线程,这种写法,会让人感到不安!
所谓线程池:就是把一堆线程都弄到一起,放进一个池子里统一管理。
(需要用的时候从里面抓合适数量的线程来do事情,不需要时再扔回去!)
这种统一管理调度,并循环利用线程的方式,就叫做线程池!
2.2》实现方式
解释:
我们在程序启动时,就会一次性地创建好一定数量的线程(作为线程池)
当有需要时,从里面抓空闲的线程来do事情,等这个线程忙完了就扔回去,这种对线程的管理方式是良好且高效的!
不像以前我们都是一需要用到线程就创建一个,然后再销毁。这样的即用即创建即不用即销毁的方式效率很低!并且,用线程池的方式来管理多线程程序是稳定的!为什么呢?因为我一开始就知道我是否能够创建项目所需要的线程个数了。
直观地说:
如果创建失败,我还能把后续线程代码崩溃导致项目造成损失这样的后果扼杀在萌芽之中呢!
如果创建成功,那是ok的,放心使用这份多线程代码即可!
若你不用线程池的方式,而是用到了再创建线程的话,指不定某一次创建线程失败(系统资源不足)就会导致整个程序崩溃了!这样就太不安全了!
As a result:(以创建线程池的方式来管理所有线程)程序启动时,一次性创建好一定数量的线程。这种方式让人更放心,程序的代码更稳定!
线程池实现:
我的线程池实现(C语言版本)
《3》线程创建数量谈
3.1》创建线程的数量极限的问题
一般来讲,一台计算机中,创建2000个线程基本就是极限;再创建就会崩溃。
3.2》创建线程数量建议
a)采用某技术开发程序时;若api接口提供商锁提供了建议(比如创建线程数量等于cpu数量,= cpu数量*2,= cpu数量*2 + 2 等等等等),我们最好都要遵照建议和指示来写代码,以确保你的程序能够高效执行。
b)创建多线程完成业务时;考虑可能被阻塞的线程数量,创建多于最大被阻塞线程数量的线程,如游戏的充值业务中,100个线程被阻塞时,开110个线程就是很合适的。
c)wjw老师建议,一般我们创建线程的数量最好不要超过500个,尽量控制在200个之内;
(当然,按照做项目写代码时,你自己测试自己的多线程代码是否足够高效运行才定下创建线程的数量,这样才是最佳的建议!毕竟,Practice make perfect!)
《4》C++11多线程课程总结
C++11在语言层面给我们引入了多线程开发的接口,这是一件非常好的事儿!因为这样无论是在windows下还是在linux下我们都能用一套代码完成对应的工作。当然,很有可能在日后的工作中还需要我们结合各个平台下对应的多线程编程接口以及C++给我们提供的接口一同来完成项目的多线程编程。这没关系,日后工作中用到了学习即可!
本课程中,wjw老师给我们详细地讲解了C++11中的一些常用且实用的多线程编程知识,而没讲解到的则是一些不常用的,少用的知识。这部分知识在我们日后的工作中若有需要可以翻阅相关的资料并查阅学习补充回来!况且C++还在不断地发展之中呢,加油吧!学 无止境~希望我们都能在Cpp开发的路上越走越远,也越走越好!coding continue~