本文共 2597 字,大约阅读时间需要 8 分钟。
我们为什么要选择多线程?大多数我们选择多线程的原因就是因为快。但
使用多线程就是在正确的场景下通过设置正确的线程来最大化程序的运行速度。在硬件级别就是充分利用CPU和I/O的利用率。
在具体场景下,通过是以下两个场景:
一个完整请求,I/O操作可以在很短时间内完成,但是CPU还有很多运算要处理,也就是说CPU计算的比例占很大一部分。
比如我们要计算1+2+…100亿的总和,很明显,这就是一个CPU密集型程序。
假如我们在【单核】CPU下,如果我们创建4个线程来分段计算,即:
由于是单核CPU,所有线程都在等待CPU时间片。按照理想情况来看,四个线程执行的时间总和与一个线程独自完成是相等的,实际上我们还忽略了四个线程上下文切换的开销;
综上,单核CPU处理CPU密集型程序,这种情况并不适合使用多线程。
此时如果在4核CPU下,同样创建四个线程来分段计算,看看会发生什么?
每个线程都有CPU来运行,并不会发生等待CPU时间片的情况,也没有线程切换的开销。理论情况来看效率提升了4倍。
如果是多核CPU处理CPU密集型程序,完全可以最大化利用CPU核心数,应用并发线程来提高效率。
与CPU密集型程序相对,一个完整请求,CPU运算操作完成之后还有很多I/O操作要做, 也就是说I/O操作占比很大部分。
在进行I/O操作时,CPU是空闲状态,所以要最大化利用CPU,不能让其是空闲状态,同样在单核CPU的情况下:
从上图中可以看出,每个线程都执行了相同长度的CPU耗时和I/O耗时,如果将上面的图多画几个周期,CPU操作耗时固定,将I/O操作耗时变为CPU耗时的3倍。这时候会发现,CPU又有空闲,这是你就可以新建线程4,来继续最大化利用CPU。
综上:
线程等待时间所占比例越高,需要越多线程;线程CPU时间所占比较越高,需要越少线程。
首先是场景问题,从上面知道我们有CPU密集型和I/O密集型两个场景,不同的场景需要的线程数也就不一样。
CPU密集型程序创建多少个线程合适呢?
对于CPU密集型程序来说,理论上线程数量=CPU核数(逻辑)就可以了,但是实际上,数量一般会设置为CPU核数+1,为什么呢?
计算机CPU密集型任务的线程恰好在某时因为发生一个页面错误或者因为其它原因而暂停,刚好有一个额外的线程,可以确保在这种情况下CPU周期不会中断工作。
I/O密集型程序创建多少个线程合适呢?
一个CPU核心的最佳线程数=(1/CPU利用率)=1+(I/O耗时/CPU耗时)。
多个CPU核心的最佳线程数=CPU核心数*(1+(I/O耗时/CPU耗时))
按照上面的公式,假如几乎全是I/O耗时,纯理论上就可以说是2N(N=CPU核数)。
如果理论都好用,那就用不着实践了,也就更不会有调优的事情出现了。不过在初始阶段,确实可以按照这个理论作为伪标准,毕竟差也不可能差太多,这样调优会更好一些。
之后具体调优阶段可以利用一些工具SkyWalking
、CAT
、zipkin
等调优工具来知道具体的I/O耗时和CPU耗时,以及CPU利用率。
假设要求一个系统的TPS(Transaction Per Second或者Task Per Second)至少为20,然后假设每一个Transaction由一个线程完成,继续假设平均每个线程处理一个Transaction的时间为4s。
如何设计线程个数,使得其可以在1s内处理完这20个transaction?
因:一个线程一个transaction时间为4s,那每秒一个线程只能处理1/4=0.25个TPS。
所:1秒内要处理20个TPS,理论线程数=20/0.25=80个。
但没有考虑到CPU数目,一般服务器的CPU核数为16或者32。如果有80个线程,肯定会带来太多不必要的线程上下文切换开销,这就需要调优,来达到最佳balance。
计算操作需要5ms,DB操作需要100ms,对于一台8核CPU的服务器,怎么设置线程数呢?
线程数=8*(1+100/5)=168个。
那么如果DB的QPS(Query Per Second)上限是1000,那么此时这个线程数又应该设置为多大呢?
因为:1s=1000ms,当前一个任务5+100=105ms,那么一个线程每秒可以处理的任务数就是1000/105。
那么168个线程每秒可以处理的任务数就是168*1000/105=1600QPS。
而因为QPS的上限是1000,所以线程数就要等比减少为168*1000/1600=105个。
同样,这也没用考虑CPU数目,接下来就是细节调优的阶段了。
因为一次请求不仅仅包括CPU和I/O操作,具体的调优过程还要考虑内存资源、网络等具体情况。
即使算出了理论线程数,但实际上CPU核数不够,会带来线程上下文切换的开销。
那么下一步就需要增加CPU核数,那盲目的增加CPU核数就一定能解决问题吗?
有一个阿姆达尔定律,它代表了处理器并行计算后效率提升的能力。
这个公式告诉我们,假如我们的串行率是5%,那么无论采用什么技术,最高也就只能提高20倍的性能。
临界区都是串行的,非临界区都是并行的,用单线程执行临界区的时间/用单线程执行(临界区+非临界区)的时间就是串行百分比。
多线程一定就比单线程高校,比如大名鼎鼎的Redis,是基于内存操作的,这种情况下,单线程可以很高效的利用CPU。而多线程的使用场景一般是存在相当比例的I/O或网络操作。
依据公式先计算,后续结合实际来调优(综合考虑CPU 内存 硬盘读写速度 网路状况等)
线程一定就比单线程高校,比如大名鼎鼎的Redis,是基于内存操作的,这种情况下,单线程可以很高效的利用CPU。而多线程的使用场景一般是存在相当比例的I/O或网络操作。
依据公式先计算,后续结合实际来调优(综合考虑CPU 内存 硬盘读写速度 网路状况等)
盲目增加CPU核数也不一定能解决我们的问题。
转载地址:http://hcwdf.baihongyu.com/