斯卡拉的演员是否和Go的协同作品相似?

如果我想移植一个使用Goroutines的Go库,那么Scala会是一个不错的select,因为它的收件箱/ akka框架与协程的性质相似吗?

不,他们不是。 Goroutines基于Tony Hoare在1978年指定的“沟通顺序进程”理论。这个想法是可以有两个进程或线程独立于另一个进程而共享一个“通道”,一个进程/线程放入数据进入和其他进程/线程消耗。 你会发现最突出的实现是Go的渠道和Clojure的core.async ,但是目前它们仅限于当前的运行时间,并且不能被分配,甚至在同一物理盒子上的两个运行时间之间。

CSP发展到包含一个静态的,forms化的过程代数来certificate代码中存在死锁。 这是一个非常好的function,但是Goroutines和core.async目前都不支持它。 如果他们这样做,在运行你的代码之前知道死锁是否可能是非常好的。 但是,CSP不能以有意义的方式支持容错function,所以开发人员必须弄清楚如何处理通道两边可能发生的故障,而这样的逻辑最终会遍布整个应用程序。

卡尔•休伊特(Carl Hewitt)在1973年指定的演员,涉及拥有自己的邮箱的实体。 它们本质上是asynchronous的,并且具有跨运行时和机器的位置透明性 – 如果你有一个actor的引用(Akka)或者PID(Erlang),你可以给它发送消息。 这也是一些人在基于Actor的实现中发现错误的地方,因为你必须引用另一个actor来发送消息,从而直接耦合发送者和接收者。 在CSP模型中,渠道是共享的,可以被多个生产者和消费者共享。 根据我的经验,这并不是什么大问题。 我喜欢代理引用的想法,这意味着我的代码没有散布如何发送消息的实现细节 – 我只发送一个,而且无论演员在哪里,它都会收到它。 如果这个节点出现故障并且演员在其他地方转世,那么理论上对我来说是透明的。

演员有另一个非常好的function – 容错。 通过按照Erlangdevise的OTP规范将参与者组织到一个监督层次结构中,您可以在应用程序中构build一个失败域。 就像价值类/ DTOs /无论你想叫他们,你可以build模失败,应该如何处理,在什么层次的层次结构。 这是非常强大的,因为你在CSP内部几乎没有故障处理能力。

参与者也是一个并发范例,在这个范例中,参与者可以在其中拥有可变状态,并保证没有multithreading访问状态,除非开发人员构build基于actor的系统意外地引入了它,例如通过将Actor注册为侦听器对于callback,或通过期货在演员内部进行asynchronous。

无耻的插件 – 我正在和Akka团队负责人Roland Kuhn一起写一本新书,叫做Reactive Design Patterns,我们将讨论所有这些以及更多。 绿色线程,CSP,事件循环,迭代器,react native扩展,演员,期货/承诺等。预计下个月初将在曼宁看到一个MEAP。

祝你好运!

这里有两个问题:

  • 斯卡拉是一个很好的select港口goroutines

这是一个简单的问题,因为Scala是一种通用语言,与其他许多可以select“port goroutine”的语言相比,它不会更糟或更好。

对于斯卡拉为什么更好或者更差的语言 (例如这里是我的),当然有许多意见 ,但这些只是意见,不要让它们阻止你。 既然Scala是通用的,那么“非常”就归结为:你可以在X语言中做的所有事情都可以在Scala中完成。 如果它听起来太宽了.. Java中的延续如何:)

  • 斯卡拉演员是否类似于goroutines

除了挑剔之外,唯一的相似之处在于它们都与并发和消息传递有关。 但这就是相似性结束的地方。

由于杰米的回答给了斯卡拉演员一个很好的概述,我将更多地关注Goroutines / core.async,但与一些演员模型介绍。

演员帮助事情“无忧分配”


“无忧”部分通常与诸如fault toleranceresiliencyavailability等术语联系在一起。

没有深入细节演员如何工作,在两个简单的术语演员必须做的:

  • 地点 :每个angular色都有其他angular色可以用来发送消息的地址/引用
  • 行为 :消息到达演员时应用/调用的函数

思考“谈话过程”,每个过程有一个参考和一个消息到达时被调用的函数。

当然还有更多的东西(例如Erlang OTP或akka docs ),但是上面的两个是一个好的开始。

在演员中获得有趣的地方是实现。 目前有两个大型的Erlang OTP和Scala AKKA。 虽然他们都旨在解决同样的事情,但有一些分歧。 让我们看看一对夫妇:

  • 我故意不使用诸如“参照透明度”,“幂等性”等术语,除了引起混淆之外别无他法,所以我们只谈论不变性(a can't change that概念)。 Erlang作为一种语言是自以为是的,而且它倾向于强大的不可变性,而在Scala中,当接收到消息时,让演员改变/改变他们的状态是很容易的。 这是不推荐的,但斯卡拉的可变性正好在你面前,人们使用它。

  • 乔·阿姆斯特朗谈到的另一个有趣的观点是,Scala / AKKA受到JVM的限制,而这个JVM并不是真正为“分布式”而devise的,而Erlang VM则是如此。 它必须处理很多事情,例如:进程隔离,每个进程与整个虚拟机垃圾收集,类加载,进程调度等等。

上述的观点并不是说一个人比另一个人好,而是要表明演员模式的纯洁性作为一个概念取决于其实施。

现在到goroutines ..

Goroutines有助于按顺序推理并发


正如已经提到的其他答案,goroutines根植于交stream顺序过程 ,这是一个“描述并发系统中交互模式的forms语言”,根据定义,它可以表示任何东西:)

我将给出基于core.async的例子,因为我知道它比Goroutines更好。 但是core.async是在Goroutines / CSP模型之后构build的,所以在概念上不应该有太多差异。

core.async / Goroutine中的主要并发原语是一个channel 。 把channel想象成“岩石上的排队”。 此通道用于“传递”消息。 任何想要“参与游戏”的过程都会创build或获取对channel的引用,并将消息放入/取出(例如发送/接收)到/从中。

免费24小时停车

大部分在通道上完成的工作通常发生在一个“ Goroutine ”或“ go block ”内部,“ 它通过它的身体并检查它进行任何通道操作,它将把身体变成一个状态机,状态机将被“停放”,并且实际的控制线程将被释放,这种方法类似于C#asynchronous中使用的方法,当阻塞操作完成时,代码将被恢复(在线程池线程上,或在JS VM中唯一的线程) “( 来源 )。

用视觉传达很容易。 这是阻塞IO执行的样子:

阻止IO

你可以看到线程大多花费时间等待工作。 这是同样的工作,但通过“Goroutine”/“去块”方法完成:

core.async

这里有两个线程做了所有的工作,4个线程在阻塞方法中做了相同的时间。

以上描述中的kicker是:“线程在没有工作时停放 ”,这意味着它们的状态被“卸载”到一个状态机,并且实际的活JVM线程可以自由地做其他工作)

注意 :在core.async中,通道可以在 “go block”之外使用,它将由一个JVM线程支持而不具有停放能力:例如,如果阻塞,则阻塞真实线程。

Go频道的力量

“Goroutines”/“go block”中的另一件大事是可以在频道上执行的操作。 例如,可以创build一个超时通道 ,它将在X毫秒内closures。 或者select/ alt! function,当与许多频道一起使用时,就像在不同频道上“准备就绪”的投票机制一样。 把它想象成一个在非阻塞IO中的套接字select器。 这里是一个使用timeout channelalt!的例子alt! 一起:

 (defn race [q] (searching [:.yahoo :.google :.bing]) (let [t (timeout timeout-ms) start (now)] (go (alt! (GET (str "/yahoo?q=" q)) ([v] (winner :.yahoo v (took start))) (GET (str "/bing?q=" q)) ([v] (winner :.bing v (took start))) (GET (str "/google?q=" q)) ([v] (winner :.google v (took start))) t ([v] (show-timeout timeout-ms)))))) 

这个代码片断是从wracer中提取出来的,它向所有三个发送相同的请求:Yahoo,Bing和Google,并返回最快的结果,如果在给定的时间内没有返回,则返回超时消息。 Clojure可能不是你的第一语言,但你不能不同意这种实现并发性的顺序

您还可以合并/扇入/传出多个频道的数据,地图/缩小/过滤/ …频道数据等等。 频道也是一等公民:您可以将频道传递到频道

去用户界面去!

由于core.async“go blocks”具有“驻留”执行状态的能力,并且在处理并发时具有非常连续的“外观和感觉”,所以JavaScript呢? JavaScript没有并发性,因为只有一个线程,对吗? 并发模仿的方式是通过1024callback。

但是并不一定要这样。 上面这个来自于wracer的例子实际上是用ClojureScript编写的,编译成JavaScript。 是的,它可以在multithreading和/或浏览器的服务器上工作:代码可以保持不变。

Goroutines与core.async

另外,还有一些实施差异(还有更多的)强调了这样一个事实:理论概念在实践中并不完全一一对应:

  • 在Go中,input一个通道,在core.async中不是:例如在core.async中,可以将任何types的消息放在同一个通道上。
  • 在Go中,你可以在频道上放置可变的东西。 这不是build议,但你可以。 在core.async中,通过Clojuredevise,所有的数据结构都是不可变的,因此通道内的数据对于它的健康感觉更安全。

那么判决是什么?


我希望上面的演员模型和CSP之间的区别。

不要引起一场激烈的战争,而是要给你另一个让我们说Rich Hickey的观点:

我仍然对演员不感兴趣,他们仍然把制作人和消费者结合在一起,是的,人们可以模仿或执行某些与演员(特别是人们经常这样做)的排队,但是由于任何演员机制已经包含一个队列,看起来很明显,队列是更原始的,应该指出的是,Clojure的并行使用状态的机制是可行的,渠道是面向一个系统的stream动方面。 “( 来源 )

然而,实际上,Whatsapp是基于Erlang OTP的,而且它似乎卖得不错。

另一个有趣的引用是Rob Pike:

缓冲的发送没有被确认给发送者,并且可以采取任意长的缓冲的信道和goroutines非常接近演员模型。

演员模特和Go的真正区别在于,频道是一stream的公民。 同样重要的是:它们是间接的,就像文件描述符而不是文件名,允许在actor模型中不那么容易expression的并发风格。 也有相反的情况。 我没有做出价值判断。 理论上这些模型是等价的。 “( 来源 )

把我的一些评论移到一个答案。 这是太长了:D(不要从杰米和tolitius的职位上拿走,他们都是非常有用的答案。)

你可以做与你在Akka的门厅做同样的事情,这是不正确的。 去信道经常用作同步点。 你不能直接在阿卡再现。 在Akka中,后同步处理必须转移到一个单独的处理程序中(用Jamie的话说:“D”)。 我会说devise模式是不同的。 你可以开一个食堂,做一些东西,然后<-等待它完成,然后继续前进。 阿卡有这样一个不太强大的forms与ask ,但ask是不是真正的阿卡方式国际海事组织。

Chans也是键入的,而邮箱不是。 这是一个很大的IMO,这对于一个基于Scala的系统来说是相当震撼的。 我知道become很难用键入的消息来实现,但也许这表明, become不是非常斯卡拉样。 我一般可以这样说阿卡。 它经常感觉就像在Scala上运行一样。 Goroutines是Go的一个关键原因。

不要误会我的意思; 我非常喜欢这个演员模特,我一般都喜欢Akka,觉得工作起来很愉快。我一般也喜欢Go(我觉得Scala很漂亮,而我觉得Go只是有用的,但是非常有用)。

但容错实际上是Akka IMO的要点。 你碰巧得到并发。 并发是程序的核心。 Go中的容错是一个单独的事情,委托deferrecover ,这可以用来实现相当多的容错性。 Akka的容错性更为正式,function丰富,但也可能更复杂一些。

尽pipe有一些相似之处,阿卡并不是Go的超集,它们在特征上有很大的分歧。 阿卡和Go在他们如何鼓励你接近问题方面是完全不同的,而在一个方面很容易的事情在另一方面是尴尬的,不切实际的,或者至less是非惯用的。 这是任何系统的关键区别。

所以把它带回到你真正的问题:我强烈build议重新思考Go界面,然后把它带到斯卡拉或阿卡(这也是不同的事情国际海事组织)。 确保你的目标环境意味着要做的事情。 一个复杂的Go库的直接端口可能不适合任何环境。

这些都是很好而彻底的答案。 但是,以一个简单的方式来看待它,这是我的观点。 Goroutines是Actor的简单抽象。 演员只是Goroutines更具体的使用案例。

您可以通过创buildGoroutine旁边的频道来实现使用Goroutines的演员。 通过判断这个频道是由Goroutine“拥有”的,你只是说Goroutine会从中消费。 您的Goroutine只需在该频道上运行收件箱 – 邮件匹配循环。 然后,您可以简单地将频道作为您的“演员”(Goroutine)的“地址”。

但是,Goroutines是一个抽象的概念,比一般的devise更具有普遍性,Goroutines可以被用于比Actor更多的任务和devise。

但是,由于演员是一个更具体的情况,因此Erlang等演员的实现可以更好地优化它们(在收件箱循环中进行轨道recursion),并且可以更容易地提供其他内置function(多进程和机器angular色) 。