如果我想移植一个使用 Goroutines 的 Go 库,Scala 会是一个不错的选择,因为它的 inbox/akka 框架在本质上类似于协程吗?
他们不是。Goroutines 基于 Communicating Sequential Processes 理论,由 Tony Hoare 在 1978 年指定。这个想法是可以有两个进程或线程相互独立但共享一个“通道”,其中一个进程/线程放置数据进入和另一个进程/线程消耗。您会发现最突出的实现是 Go 的通道和 Clojure 的core.async,但此时它们仅限于当前运行时,不能分发,即使在同一个物理机器上的两个运行时之间也是如此。
CSP 演变为包含一个静态的、正式的过程代数,用于证明代码中死锁的存在。这是一个非常好的功能,但 Goroutines 和core.async当前都不支持它。如果他们这样做,那么在运行您的代码之前知道是否可能发生死锁会非常好。但是,CSP 并不以有意义的方式支持容错,因此您作为开发人员必须弄清楚如何处理可能发生在通道两侧的故障,而这种逻辑最终会散布在整个应用程序中。
演员,正如卡尔休伊特在 1973 年所指定的,涉及拥有自己邮箱的实体。它们本质上是异步的,并且具有跨越运行时和机器的位置透明度——如果您有一个参与者的引用 (Akka) 或 PID (Erlang),您可以向它发送消息。这也是一些人在基于 Actor 的实现中发现错误的地方,因为您必须拥有对另一个 Actor 的引用才能向它发送消息,从而直接耦合发送方和接收方。在CSP模型中,通道是共享的,可以被多个生产者和消费者共享。根据我的经验,这不是什么大问题。我喜欢代理引用的想法,这意味着我的代码不会充斥着如何发送消息的实现细节——我只发送一个,无论演员位于哪里,它都会接收它。
Actors 还有一个非常好的特性——容错。通过按照 Erlang 中设计的 OTP 规范将参与者组织到监督层次结构中,您可以在应用程序中构建故障域。就像值类/DTO/任何你想调用它们的东西一样,你可以对失败、应该如何处理以及在层次结构的哪个级别进行建模。这非常强大,因为您在 CSP 内部几乎没有故障处理能力。
Actors 也是一种并发范式,Actor 内部可以有可变状态,并保证没有多线程访问该状态,除非构建基于 Actor 的系统的开发人员不小心引入了它,例如通过将 Actor 注册为侦听器用于回调,或通过 Futures 在 actor 内部进行异步处理。
无耻的插件 - 我正在与 Akka 团队的负责人 Roland Kuhn 一起写一本名为 Reactive Design Patterns 的新书,我们将在其中讨论所有这些以及更多内容。绿色线程、CSP、事件循环、迭代器、反应式扩展、Actor、Futures/Promises 等。预计在下个月初之前会看到关于 Manning 的 MEAP。