Scala “是怎么回事?”;变成;用支持参与者模型的语言实现?

Scala “是怎么回事?”;变成;用支持参与者模型的语言实现?,scala,concurrency,erlang,actor,Scala,Concurrency,Erlang,Actor,Gul Agha在他的技术报告“Actors:分布式系统中的并发计算模型”中很好地描述了actor模型 在第49页,他解释了“成为”命令: 变成 在调用“成为X”后,一个参与者将把他的所有消息转发到另一个参与者的邮箱(X) 但是,我不确定这是如何在Erlang和Scala等语言中实现的(它是如何实现的)。是不是有什么东西需要我手动编码?效率呢?Agha显示了使用消息传递的堆栈实现。每次执行pop或push时,都会向某个参与者添加一个或多个转发链接。。。在几十万次操作之后,我希望这样的实现会花费

Gul Agha在他的技术报告“Actors:分布式系统中的并发计算模型”中很好地描述了actor模型

在第49页,他解释了“成为”命令:

变成
在调用“成为X”后,一个参与者将把他的所有消息转发到另一个参与者的邮箱(X)

但是,我不确定这是如何在Erlang和Scala等语言中实现的(它是如何实现的)。是不是有什么东西需要我手动编码?效率呢?Agha显示了使用消息传递的堆栈实现。每次执行pop或push时,都会向某个参与者添加一个或多个转发链接。。。在几十万次操作之后,我希望这样的实现会花费太多的时间转发消息,而不会做实际的工作,除非在幕后进行一些很好的优化


所以我的问题是:在Erlang、Scala(以及其他语言的库)这样的典型参与者语言中,转发(或“变成”)是如何实现的?

它不是直接在Erlang中实现的,但您可以编写一个简单的
变成
函数来接收消息,将其转发到另一个进程,然后调用自己:

become(Pid) ->
    receive
        Msg -> Pid ! Msg
    end,
    become(Pid).
(工业实力的版本可能需要处理信号和其他零碎的东西,但这是它的本质。)

从外部世界的角度来看,调用
been(Pid)
将有效地将调用进程转变为进程
Pid

这并不能解决您强调的问题,重复调用
变成
会导致转发链的增长。然而,这在Erlang中通常不会发生,我也不确定Erlang的进程如何映射到Actor模型上。

看看Agha的论文“Actors:分布式系统中并发计算的模型”的第12页(我拥有的PDF副本的第26页)。“Been”是他的Actor语言,是如何指定#2,Actor的新行为。将消息转发给其他参与者只是许多可能的新行为之一

我认为,如果您想要转发行为,那么Scala actors与Erlang基本上处于同一条船上。在幕后,Scala的“react”和“reactWithin”工作起来很像Been,因为react块定义的部分函数是参与者的新行为,但我不确定相似性是否是有意的


大多数(全部?)的“actor”实现与Hewitt的actor模型和Agha的actor语言有相当大的差异。IIRC在Agha的langauge中指定演员行为的语言部分甚至不是图灵完整的。当你考虑演员的配置时,整个语言只会变成图灵完成。可以说,演员模型和当前演员框架之间的关系有点像ScMLT中的对象定位与C++中的对象定位的关系。有一些概念转移和类似的术语,但在细节上它们非常非常不同。

演员是一个反变的共同创造者,所以“成为”只是comap

换句话说,T类型消息上的参与者基本上是(T=>Unit)类型的函数。这就是简单的函数组合(也许是身份函数)

它在Scalaz中实现:

val a1 = actor(a => println(a))
val a2 = a1.comap(f)
actor a2将f应用于其消息,然后将结果发送给a1。

此处选择Erlang

在基本层面上,有两个选项可用。如果您只想使用
been
来更改给定进程的行为(请参阅第2.1.3节列表中的第2点),那么问题只是使用不同的递归函数调用下一个循环:

loop(State) ->
     receive
          {normal, Msg} -> loop(State);
          {change, NewLoop} -> NewLoop(State)
     end.
假设
NewLoop
是一个高阶函数,每当您将消息
{change,NewLoop}
发送到最初运行函数
loop/1
的进程时,它将使用
NewLoop
作为其定义

第二个选项是希望流程充当代理(并更改行为)的选项。这与马塞洛·坎托斯的建议相似。只需让流程循环并将消息转发到新流程(窃取他的代码):


从理论上讲,这符合论文的要求。然而,在实践中,在现实生活中的Erlang系统中使用第二个选项存在风险。在两个进程之间的通信中,通常使用发送方的进程标识符“标记”消息,并使用接收方的进程标识符标记回复。可以交换以下消息(这不是代码,只是一个手写符号):

此测试代码应该更安全,因为依赖于第一个进程的进程将获得与
been(Pid)
Pid
表示的错误消息相同的错误消息,这使得路由相当透明。但是,我不能保证这在实际应用程序中长期有效

尽管在概念上可以很简单地表示和执行像
变成
这样的事情,但Erlang的标准库并没有真正考虑到第二个用例。对于实际应用程序,我只能推荐第一种方法,目前存在的每个Erlang应用程序都广泛使用这种方法。第二种情况不常见,可能会导致问题



**最后一个示例中对函数
been/1
的调用可能是
?模块:been(Pid)
,以避免将来与热代码加载相关的潜在崩溃*

Akka的参与者有一个“热交换”概念,您可以向参与者发送一个新的PartialFunction,以替换其现有的消息处理程序。上一个会被记住并可以恢复。在上搜索“热交换”以了解详细信息。

函数组合语义似乎与本页上对BENG的其他描述不一致,我在上读到该描述
loop(State) ->
     receive
          {normal, Msg} -> loop(State);
          {change, NewLoop} -> NewLoop(State)
     end.
become(Pid) ->
    receive
        Msg -> Pid ! Msg
    end,
    become(Pid).
A = <0.42.0> <-- process identifier
B = <0.54.0>,
A: {A, "hello"},
B: {B, "hi"},
A: {A, "how are you?"}.
B: {B, "Fine!"}.
loop(State) ->
     receive
          {normal, Msg} -> loop(State);
          {become, Pid} ->
              process_flag(trap_exit, true), % catch exit signals as messages
              link(Pid),                     % make it so if one process crashes, the other receives the signal
              become(Pid)
     end.

become(Pid) ->
    receive
        {'EXIT', Pid, killed} -> exit(self(), kill);  %% uncatchable termination
        {'EXIT', Pid, normal} -> ok;                  %% die normally too
        {'EXIT', Pid, Reason} -> exit(Reason);        %% die for the same reason as Pid
        Msg -> Pid ! Msg                              %% forward the message
    end,
    become(Pid).