为什么要用多线程

以前我认为多线程的作用就是提升性能。实际上,多线程并不一定能提升性能(甚至还会降低性能);多线程也不只是为了提升性能。多线程主要有以下的应用场景:

1、避免阻塞(异步调用)

单个线程中的程序,是顺序执行的。如果前面的操作发生了阻塞,那么就会影响到后面的操作。这时候可以采用多线程,我感觉就等于是异步调用。这样的例子有很多:

ajax调用,就是浏览器会启一个新的线程,不阻塞当前页面的正常操作;

流程在某个环节调用webservice,如果是同步调用,则需要等待webservice调用结果,可以启动新线程来调用,不影响主流程;

android里,不要在uithread里执行耗时操作,否则容易引发ANR;

创建工单时,需要级联往其他表中插入数据,可以将级联插入的动作放到新线程中,先返回工单创建的结果……

2、避免CPU空转

以httpserver为例,如果只用单线程响应HTTP请求,即处理完一条请求,再处理下一条请求的话,CPU会存在大量的闲置时间

因为处理一条请求,经常涉及到RPC、数据库访问、磁盘IO等操作,这些操作的速度比CPU慢很多,而在等待这些响应的时候,CPU却不能去处理新的请求,因此httpserver的性能就很差

所以很多web容器,都采用对每个请求创建新线程来响应的方式实现,这样在等待请求A的IO操作的等待时间里,就可以去继续处理请求B,对并发的响应性就好了很多

当然,这种设计方式并不是绝对的,现在像node.js、Nginx等新一代httpserver,采用了事件驱动的实现方式,用单线程来响应多个请求也是没问题的。甚至实现了更高的性能,因为多线程是一把双刃剑,在提升了响应性的同时,创建销毁线程都是需要开销的,另外CPU在线程之间切换,也会带来额外的开销。避免了这些额外开销,可能是node.js等httpserver性能优秀的原因之一吧

3、提升性能

在满足条件的前提下,多线程确实能提升性能

打一个比方,多线程就相当于,把要炒的菜放到了不同的锅里,然后用不同的炉来炒,当然速度会比较快。本来需要先炒西红柿,10分钟;再炒白菜10分钟;加起来就需要20分钟。用了多线程以后,分别放在2个锅里炒,10分钟就都炒好了

基本上,需要满足3个条件:

第1,任务具有并发性,也就是可以拆分成多个子任务。并不是什么任务都能拆分的,条件还比较苛刻

子任务之间不能有先后顺序的依赖,必须是允许并行的

比如

a = b + c
d = e + f

这个是可以并行的;

a = b + c
d = a + e

这个就无法并行了,第2步计算需要依赖第1步的计算结果,即使分成2个线程,也不会带来任何性能提升

另外,还不能有资源竞争。比如2个线程都需要写一个文件,第1个线程将文件锁定了,第2个线程只能等着,这样的2个子任务,也不具备并发性;执行sychronized代码,也是同样的情况

第2,只有在CPU是性能瓶颈的情况下,多线程才能实现提升性能的目的。比如一段程序,瓶颈在于IO操作,那么把这个程序拆分到2个线程中执行,也是无法提升性能的

第3,有点像废话,就是需要有多核CPU才行。否则的话,虽然拆分成了多个可并行的子任务,但是没有足够的CPU,还是只有一个CPU在多个线程中切换来切换去,不但达不到提升性能的效果,反而由于增加了额外的开销,而降低了性能。类似于虽然把菜放到了2个锅里,但是只有1个炉子一样

如果上述条件都满足,有一个经验公式可以计算性能提升的比例,叫阿姆达尔定律:

速度提升比例=1/[(1-P)+(P/N)],其中P是可并行任务的比例,N是CPU核心数量

假设CPU核心是无限的,则公式简化为1/(1-P)

假设P达到了80%(已经非常高了),那么速度提升比例也只能达到5倍而已

相关推荐