[杂谈]Actor 模型介绍

发布于:2019-09-05

杂谈

Actor模型

Actor模型定义

Actor模型可以说是并发编程中非常常见的一种模型,该模型是Carl Hewitt在1973年提出的。

Actor模型如何工作

  1. Actor是独立的,每个Actor只管理自己的内部数据,对外暴露一个通信用的邮箱
  2. Actor都是通过向另一Actor暴露的邮箱发送消息来进行通信
  3. Actor之间的通讯行为是异步的

Actor模型中的Actor可以做什么

  1. 接收消息并进行相应处理
  2. 创建新的Actor
  3. 发送消息给另一个Actor

Actor实现

Erlang的Actor实现

  1. Erlang的Actor模型是基于Erlang进程实现的
  2. Erlang的Actor在死亡后会立刻进行垃圾处理
  3. Erlang的Actor直被Erts内部的POSIX线程调度

Akka的Actor实现

  1. Akka的Actor模型以类为基础,通过Java的类库来实现
  2. Akka的Actor在死亡后,需要等待JVM进行GC时才进行垃圾处理
  3. Akka的Actor是通过Java的线程池调度

两种实现的差异

Actor调度

Erlang的调度方式是抢占式公平调度(Erts强行切换),Akka的调度是协作式调度(完全依赖Actor主动放弃调度器)。

Erlang的Actor调度会受到CPU数量和Actor数量影响,具体例子可以看下面:

  • 我们的CPU为2Core时,process数为200,每个process平均获得CPU的能力 1/200 * 2 = 1% 。
  • 我们的CPU为4Core时,process数为200,每个process平均获得CPU的能力 1/200 * 4 = 2% 。
  • 我们的CPU为8Core时,process数为1000,每个process平均获得CPU的能力 1/1000 * 8 Core = 0.8%。

也就是说,Erlang在进程数不变的时候,增加CPU会增加每个Erlang进程的执行时,而Akka的调度是协作式的调度,这就代表着我们无法得到上面例子中的算式,当一个调度器上的某个Actor在做一个非常长时间的计算,完全由可能让调度器上的其它Actor不能按时调度。

IO操作

Erlang的Actor在执行IO的时候会进入等待状态,放弃调度线程,Akkaz不使用封装后的IO操作时会一直占用调度线程,使用封装的IO操作时才会放弃调度线程。 Erlang因为是Erts提供的IO操作,相对会比较统一,但是如果使用自己编写的Nif或Driver就需要注意是否存在同步的IO操作,因为这种原生的IO操作会让Actor一直占用调度线程。 因此在Akka的Actor中,尽量不要使用Java提供的同步IO操作,而应该使用Akka提供的异步IO操作。