并发模型有两种,一种是并行工作者模型,一种是流水线模型。
一、所谓并行工作者模型,举个例子来说,就好比汽车厂里面的工人,每个工人专门负责一辆车子的生产,从头到尾负责所有的工作。就好像是线程,拿到了一个请求,后面这个请求的所有工作都由这个线程来处理,直到所有这个请求的所有工作都结束。
nginx的workprocessor应该就是这种模型,某个linux进程拿到了用户请求,然后负责这个请求的处理,高级点的地方就是用了非阻塞的IO,就好比这个汽车工人手头有好几辆车在处理,某辆车喷漆需要等待,那么这个工人就去他负责的另一辆车上装发动机。这种模型非常常见,比如Java里面的Executor框架,只需要把Task交给某个线程来执行就ok了。
这种模型的好处是什么呢?那就是,他很简单,逻辑清晰,任务交给某个线程就可以了,并且可以很简单的增加工作者来提高并行度(CPU足够的情况下),或者使用非阻塞的IO(比如Nginx,这样workprocessor数量只需要和cpu数量接近就可)。
但是这有什么问题呢?最大的问题就是存在共享状态,在访问共享状态的时候会存在竞态,处理不好还会发生死锁。而进行同步又会造成性能损失甚至导致线程切换造成开销。当然也有很多方法来避免这种问题,比如非阻塞算法和不可变对象。
还有一个问题就是每个工作者内部无法保存这些共享状态,导致每次都要重新读取这些状态,最直观的就是cpu缓存失效,Java内存模型对这块讲的比较多。
二、还有一种模型指的是流水线模型,还是拿上面那个汽车工人的例子来说。
现在汽车厂改进生产流程,使用了生产线来进行生产,对于一辆汽车,将他分成许多道工序,每个工人负责其中的一个工序。当某个工人完成了喷漆工作,油漆还没干,这时候他不等待油漆干,就转而开始另一辆车的喷漆工作,并把这辆车交给下一道工序。这辆车在流水线上油漆就就干掉了,到了下一道工序了就可以直接工作了。
工人们是分工协同作业。
同时,整个工厂只需要一条生产线就可以快速生产,意味着某辆汽车的生产过程中的工具设备他只需要一套就够了。如果是并行工作者,那就意味着有几个工人就要几套工具,这个成本太大,当然你也可以少几套工具,大家共享几套工具,但这也有同步的问题,都要用某个工具时必须等别人用完了才能用。
Actor模型就是一种流水线模型。
流水线模型可以很好地解决并行工作者的一些问题。首先最大的好处就是共享状态消失了,由于某个请求都按步骤分给多个线程处理了,可以做到某个步骤只有一个线程在处理,这样也就意味着共享状态消失了。其次,共享状态的消失,使得每个线程还可以缓存自己这个步骤处理所需的状态,避免状态的反复读取。
唯一的坏处就是写代码的人很痛苦,读代码的人也痛苦。要把某个请求按流程进行切分(切分的时机常常是出现阻塞的时候,比如IO,然后使用无阻塞IO),分布到不同的线程上,相当于把处理流程的代码分开了,这个复杂度就上去了,相比前面那种所有业务代码都写一起,然后由多个线程执行,读代码的人看了流水线模型的代码估计也要吐了。
当然,每个模型没有谁好谁坏之分。孰好孰坏,到底用哪个模型,还是取决于你具体做什么。