CPU不能变得更快了。现在我们有了多核CPU,如果我要榨干现有硬件的每一分性能,我们就需要让代码并行运行。数十年的难以捉摸的bug让无数开发者头疼不已,thread
并不是前进的方向。但是不用怕,时至今日我们有了优秀的替代者,而我今天就要向你展示其中之一:Actor模型。
模型
Actor模型是处理并行计算的概念性模型。它定义了系统组件行为和交互的通用规则。Erlang
大概是使用此模型的最为有名的语言。我会试着专注模型本身而不是它在不同语言或lib中的具体实现。
Actor
Actor是计算的原始单位。它可以接受消息,并在此基础上做出某种计算。
这个思想与我们在面向对象语言里使用的东西极为相似:对象可以接受消息(方法调用)并且基于其接受的消息做出某些事情(调用的是哪个方法)。
但最大的差别在于,Actor之间是相互隔离的,它们不共享内存。Actor还可以轻松维持一个私有其他Actor无法直接改变的私有状态。
独木非林
单个Actor不算是Actor。它们必须成系统地出现。在Actor模型中,任何事物都是一个Actor,而且它们需要有地址,这样Actor之间就可以相互发送消息。
Actors的信箱
理解这一点很重要,尽管同一时间可以运行多个Actor,但是一个Actor只会顺序地处理消息。这意味着如果你要给同一个Actor发送三条消息,这些消息的处理只会一个接一个进行。要想让三条消息并行处理,你需要创建三个Actor,并且给每一个Actor都发送一条消息。
消息送达给Actor的形式是异步的,这就需要在处理消息时存储其他消息。信箱就是这么个地方,存储着这些消息。
Actor之间通过发送异步消息相互交流。这些消息直到被处理前会存储在其他Actor的信箱当中。
Actor的能力
当一个Actor接受到消息时,它可以做出如下3件事情之一:
- 创建更多的Actor
- 向其他Actor发送消息
- 指定要对下一条消息做什么事
前两件事很直接,但是最后一件事就有趣了。
我之前说过Actor可以持有私有状态,“指定要对下一条消息做什么事”基本上就是在接受到下一条消息时定义出这个状态。或者说得更明白点,就是Actor会如何改变此状态。
假设我们有一个Actor,它的行为就像是计算器一样,初始状态就是数字0
。当这个Actor接收到add(1)
的消息时,它不会改变原有的状态0
,而是指定在下一条消息接收时,这个状态会变成1
。
译者 有点像“Copy-On-Write”这种形式,不就地改变原有状态。
容错性
Erlang
引入了“他错由他错”的哲学。这一思想就是说,你不应该防御性地写代码,不应该去预测未来可能发生的错误然后进行处理,原因很简单,因为你不可能面面俱到。Erlang
所做的只是任由程序崩溃,但是需要有人来监管这部分关键代码,这个人的唯一责任就是知道崩溃发生时应该做什么(比如把这块代码重设到一个稳定的状态),而正是Actor模型使这一切有可能成为现实。
每一行代码都是在某个process
(Erlang
中的Actor)的内部运行。这个process
是完全孤立的,它的状态不会被其他process
所影响。同时还存在一个监管者,它也是一个process
(还记得我说过任何事物都是一个Actor吗?),可以在被监管的process
崩溃时做出行动。
这样就可以构建出一个可以“自愈”的系统,也就是说如果Actor因为种种原因,走向了不可预知的状态,并且崩溃了,监管者就会介入,试着把它重新放入一致的状态中(有很多策略可以使用,最常用的一个就是把Actor重置为初始状态)。
分布式
另一个有趣的话题就是Actor模型不管我发送信息到何方,可以是本地也可以是远程的节点。
想想看,如果Actor仅仅是一块代码加一个信箱和一个内部状态,它能做的就是响应消息,谁会管它到底运行在什么机器上呢?只要我们想办法送达消息就行了。
这可以允许我们创建多机器的系统,而且在单点故障时可以帮我们进行恢复。
进一步学习以及更多资源
以上就是对这一概念模型的一个速览,很多优秀的语言都使用到了它,比如Erlang
和Elixir
,还有像AKKA
(适用JVM)和Celluloid
(适用Ruby)这样的库。
剩下的内容就是作者的Promoting了,你可以去原文看。