并发编程模型

并发模型设定了系统中的并发线程如何协作完成被分配的任务。不同的并发模型存在不同的业务拆分与线程协作方式。

一 并行工作者模型


 
在并行工作者模型中,委派者(Delegator)将作业分派给不同的工作者,每个工作者独立完成整个任务作业。工作者大多是以多线程的方式并行运行。

1.1 并行工作者模型的优点

简单易懂,往往只需要增加工作者来提高系统并行处理能力。例如:网络爬虫系统。

1.2 并行工作者模型的缺点

1)共享状态可能会很复杂。 

并行工作者模型可能需要访问共享数据,这些共享数据可能在内存中或者数据库中,涉及到对共享数据的存取时,往往需要进行加锁,从而使线程出现竟态死锁以及很多其他共享状态的并发性问题。同时,在执行需要访问共享数据结构部分的代码时,高竞争基本上会导致执行时出现一定程度的串行化。

2)无状态的工作者

如果共享数据能够被系统中得其他线程修改,那么所有工作者在每次使用共享数据之前都需要重新读取。不论这些共享数据是存在于内存中还是数据库中,工作者都无法在内部保存这个状态称为无状态的工作者,每次都重新读取,特别是这些共享数据存储在外部数据库中的时候,将会导致性能下降。

3)任务顺序是不确定的

由于每一个线程独立完成整个任务,所以作业执行顺序是不确定的。无法保证哪个作业最先或者最后被执行,使得很难在任何特定的时间点推断系统的状态,这也使得它也更难保证一个作业在其他作业之前被执行。

二 流水线工作模式(反应器、事件驱动模型)

在流水线工作模型中,每个工作者只负责作业中的部分工作,当完成了自己的这部分工作时工作者会将作业转发给下一个工作者。


在有时候作业甚至也有可能被转发到超过一个工作者上并发处理。比如说,作业有可能被同时转发到作业执行器和作业日志器。

流水线模型的channels 模型是比较灵活一种模型,其工作者之间不直接进行通信。他们只在不同的通道上发布自己的事件,而不需知道下一个工作者,其他工作者可以在这些通道上订阅或者取消订阅,从而实现对流水线作业的下一步处理,这使得工作者之间具有松散的耦合。

2.1 流水线模型的优点 ---- 对应了并行工作者模型的缺点

1)无需共享的状态

因为流水线工作者都是在自己的线程中运行,并且不会和其他工作者共享状态,这就好像是单线程在处理工作,也就没有了并行工作者出现的共享状态的复杂性。

2)有状态的工作者

由于没有共享状态的维护,所以工作者可以在运行时保存自己需要的数据在内存中(由于现代CPU多核的设计,CPU缓存的存在将大大提升这种状态的存取),变成有状态的,只需在最后将更改写回到外部存储系统,这将有效的提升性能。

3)合理的作业顺序
 
基于流水线并发模型实现的并发系统,在某种程度上是有可能保证作业的顺序的。作业的有序性使得它更容易地推出系统在某个特定时间点的状态。更进一步,你可以将所有到达的作业写入到日志中去。一旦这个系统的某一部分挂掉了,该日志就可以用来重头开始重建系统当时的状态。按照特定的顺序将作业写入日 志,并按这个顺序作为有保障的作业顺序.

2.2 流水线模型的缺点

流水线并发模型最大的缺点是作业的完整执行被分布到多个工作者上,而基于事件的驱动又往往采用回调的方式进行处理。这不仅加大了代码的编写阅读难度,并且难以进行追踪和确保在回调过程中只访问他需要的数据,即所谓的回调地狱

3 函数式并行(Functional Parallelism)

在面向对象思想产生之前,函数式编程已经有了数十年的历史, 但是更多地都是在大学中,在实验室中应用,而很少真的应用到真实的生产环境。由于面向对象的多核并行程序对共享数据的处理需要付出很大的代价,函数式编程就被推到了前线,无论是冷门的Haskell,Erlang,还是Scala,F#,都是函数式编程成功的典型。Java7中的java.util.concurrent包里包含的ForkAndJoinPool以及Java8中并行streams都对函数式编程的实现有帮助。

在函数式编程中,函数是基本单位,他几乎被用作一切,包括最简单的计算,甚至连变量都被计算所取代。在函数式编程中,变量只是一个名称,而不是一个存储单元,这是函数式编程与传统的命令式编程最典型的不同之处。

函数式编程是无状态的。函数式编程没有循环而是递归,因为循环是在描述我们该如何地去解决问题,这也意味着存在状态的保存,而递归是在描述这个问题的定义。

在传统的命令式编程中,变量/对象被作为状态的存储单元,而在函数式编程中,变量是不变的,他的状态是保存在函数的参数中,作为函数的附属品,函数调用通过尾递归的方式解决无状态的问题。尾递归就是不要保持当前递归函数的状态,而把需要保持的东西全部用参数给传到下一个函数里,这样就可以自动清空本次调用的栈空间,这样一来也就不用担心栈空间内存溢出的问题了。

总体地说,其实函数式编程最适合地还是解决局部性的数学小问题,要让函数式编程来做CRUD,来做我们传统的逻辑性很强的Web编程,就有些免为其难了。

就像如果要用scala完全取代今天的Java的工作,我想恐怕效果会很糟糕。而让Scala来负责底层服务的编写,恐怕再合适不过了。

猜你喜欢

转载自pzh9527.iteye.com/blog/2383506
今日推荐