如果我想要移植一个使用Goroutines的Go库,那么Scala是否是一个好选择,因为它的inbox/akka框架在性质上类似于coroutines?
如果我想要移植一个使用Goroutines的Go库,那么Scala是否是一个好选择,因为它的inbox/akka框架在性质上类似于coroutines?
core.async
,但目前它们仅限于当前运行时,并且不可以在同一物理盒上的两个运行时之间进行分布式处理。core.async
都不支持它。如果有一天他们支持了,那么在运行您的代码之前知道死锁是否可能发生将是极好的。然而,CSP不能以有意义的方式支持容错性,因此作为开发人员,你必须想办法处理可能发生在通道两侧的故障,并且这样的逻辑最终会被散布在整个应用程序中。演员还有另一个非常不错的特性 - 容错性。按照Erlang中OTP规范将演员组织成监督层次结构,您可以在应用程序中构建一定程度的故障域。就像价值类/ DTOs /任何您想称呼它们的东西一样,您可以对失败进行建模,了解应该如何处理以及在层次结构的哪个级别上处理。这非常强大,因为CSP内部几乎没有故障处理能力。
演员也是并发编程范式,其中演员可以在其内部具有可变状态,并保证无多线程访问该状态,除非基于演员的系统开发人员意外地引入它,例如通过注册Actor作为回调的侦听器或通过Futures在演员内部进行异步操作。
厚颜无耻地打广告 - 我正在与Akka团队负责人Roland Kuhn写一本新书,名为Reactive Design Patterns,其中我们讨论了所有这些以及更多内容。Green threads、CSP、事件循环、Iteratees、Reactive扩展、Actors、Futures/Promises等。预计下个月初在Manning上看到MEAP。
<-
等待完成后再继续。Akka有一种不太强大的形式,称为ask
,但在我看来,ask
并不是真正的Akka方式。通道也具有类型,而邮箱则没有。 - Rob Napier这里有两个问题:
goroutines
移植的好选择吗?这是一个简单的问题,因为Scala是一种通用语言,与许多其他你可以选择的语言一样好或不好来“移植goroutines”。
当然,对于Scala作为一种语言更好或更差的观点有很多(例如here是我的),但这些只是观点,请不要让它们阻止你。 由于Scala是通用的,它“几乎”归结为:在语言X中你能做的所有事情,你都可以在Scala中做。如果听起来太广泛了...那么continuations in Java怎么样?
goroutines
相似吗?唯一的相似之处(除了小毛病)是它们都涉及并发和消息传递。但这就是相似之处的结束。
由于Jamie的回答已经很好地概述了Scala actors,因此我将更加专注于Goroutines/core.async,但是会简单介绍一下actor模型。
“无忧”的代码通常与以下术语相关联:容错性
,弹性
,可用性
等。
不深入讲解演员如何工作,简单来说,演员涉及以下两个方面:
可以将其视为“对话进程”,其中每个进程都具有引用和在消息到达时调用的函数。
当然还有更多内容(例如,请查看Erlang OTP或akka docs),但以上两点是一个良好的起点。
演员变得有趣的地方在于实现。目前有两个重要的实现,即Erlang OTP和Scala AKKA。虽然它们都旨在解决同样的问题,但存在一些差异。让我们看看其中的一些:
我有意不使用诸如“引用透明”、“幂等性”等术语,这些术语除了造成混淆没有任何好处,所以让我们只谈论不可变性[一个“不能改变”的概念]。作为一种语言,Erlang是有偏见的,并且它倾向于强烈的不可变性,而在Scala中,当接收到消息时,很容易制作会更改/突变其状态的Actor。虽然不建议这样做,但是Scala中的可变性就在你面前,人们确实使用它。
Joe Armstrong谈到的另一个有趣的观点是,Scala/AKKA受到JVM的限制,而JVM并没有真正考虑“分布式”,而Erlang VM则考虑到了。这与许多因素有关,例如:进程隔离、每个进程与整个VM垃圾收集、类加载、进程调度等等。
以上内容的重点不是说哪一个比另一个更好,而是要表明Actor模型纯度作为一个概念取决于其实现方式。
现在来谈谈Goroutines..
core.async
是在 Goroutines/CSP 模型之后构建的,因此概念上应该没有太多区别。channel
。将 channel
视为“岩石上的队列”。使用此通道来“传递”消息。任何想要“参与游戏”的过程都会创建或获取对 channel
的引用,并将消息放入/从中取出(例如发送/接收)。这里只用了2个线程完成了4个线程在阻塞模式下的所有工作,同时花费相同的时间。
上述描述中的关键是:"当线程没有工作时,它们会被停放",这意味着它们的状态被"卸载"到一个状态机中,实际的JVM线程可以自由地进行其他工作(source提供了很好的可视化效果)
注意:在core.async中,通道可以在"go block"之外使用,这将由一个没有停车能力的JVM线程支持:例如,如果它被阻塞,它将阻塞真正的线程。
在“Goroutines” /“go blocks”中的另一个重要事项是可以对通道执行的操作。例如,可以创建一个timeout channel,它将在X毫秒后关闭。或者使用select/alt!函数,与许多通道一起使用时,它就像跨不同通道的“准备好了吗”轮询机制。将其视为非阻塞IO中的套接字选择器。以下是同时使用timeout channel
和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))))))
您还可以从/向许多通道合并/扇入/扇出数据,映射/减少/过滤/...通道数据等。通道也是一等公民:您可以将通道传递给通道。
由于核心.async "go块"具有“停放”执行状态的能力,并且在处理并发时具有非常顺序的“外观和感觉”,那么JavaScript呢?JavaScript中没有并发,因为只有一个线程,对吗?并发的模拟方式是通过1024个回调函数。
但事实并非如此。上述示例来自wracer,实际上是用ClojureScript编写的,可以编译成JavaScript。 是的,它将在具有许多线程和/或浏览器的服务器上工作:代码可以保持不变。同样,有几个实现差异[还有更多]强调理论概念在实践中并不完全是一对一的:
Actor模型和Go之间真正的区别在于,通道是一等公民。同样重要的是:它们是间接的,就像文件描述符而不是文件名,这使得并发的风格不容易在Actor模型中表达出来。也有一些情况恰恰相反;我不做价值判断。理论上,这些模型是等效的。"(来源)
chan
启动goroutine,执行一些操作,然后<-
等待它完成后再继续进行。 Akka具有较弱的形式,其中包括 ask
,但是 ask
不是真正的Akka方式,在我看来。 become
很难使用类型化消息实现,但也许这表明 become
与Scala不太像。我可以这样说Akka通常是这样的。它经常感觉像是自己的东西,碰巧在Scala上运行。Goroutines是Go存在的关键原因。 defer
和 recover
,可以用于实现相当多的容错性。 Akka的容错性更加正式和功能丰富,但它也可能会更加复杂。我们可以说,在Actor模型中,可寻址的实体是Actor,即消息的接收者。而在Go通道中,可寻址的实体是通道,即消息流动的管道。
在Go通道中,您将消息发送到通道中,任意数量的接收者都可以监听,并且其中一个将接收消息。
在Actor中,只有一个Actor将接收到您发送的消息。