在这里寻找Erlang。
在基本级别上,有两个选项可用。如果您只想become
用来更改给定进程的行为(参见第 2.1.3 节列表的第 2 点),那么只需使用不同的递归函数调用下一个循环即可:
loop(State) ->
receive
{normal, Msg} -> loop(State);
{change, NewLoop} -> NewLoop(State)
end.
假设NewLoop
是一个高阶函数,每当您将消息发送{change, NewLoop}
到最初运行该函数的进程时loop/1
,它将NewLoop
用作其定义。
第二个选项是您希望进程充当代理(并更改行为)的选项。这与 Marcelo Cantos 的建议类似。只需让流程循环并将消息转发到新的(窃取他的代码):
become(Pid) ->
receive
Msg -> Pid ! Msg
end,
become(Pid).
从理论上讲,这可以满足论文的要求。然而,在实践中,在现实生活中的 Erlang 系统中使用第二个选项是有风险的。在两个进程之间的通信中,使用发送者的进程标识符“标记”消息是一个常见的概念,并且回复将使用接收者的进程标识符进行标记。可以交换以下消息(这不是代码,只是手写符号):
A = <0.42.0> <-- process identifier
B = <0.54.0>,
A: {A, "hello"},
B: {B, "hi"},
A: {A, "how are you?"}.
B: {B, "Fine!"}.
因此,当A
期望来自 的消息时B
,它将能够通过使用诸如{B, Message}
. 在转发消息的情况下,该寻址方案变得无效并且被简单地破坏。
另一种方法是使用引用 ( make_ref()
) 作为寻址方案来匹配返回的 Pid 之上的消息。这将通过使用两个不同的实体将 Pid 作为地址和标识符的使用分开。
即使寻址是安全的,还有另一个问题:进程依赖性。命名进程、崩溃进程、监视器等会发生什么?客户端进程可能有监视器、链接和其他所有设置,以确保在没有得到通知的情况下不会出现任何问题。通过修改路由过程以捕获退出信号并转发它们,应该可以使事情变得更安全:
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).
Pid
这个经过测试的代码应该更安全,因为依赖于第一个进程的进程将收到与in表示的相同的错误消息become(Pid)
,从而使路由变得相当透明。但是,我不能保证这在现实生活中的应用程序中长期有效。
尽管有可能并且在概念上足够简单来表示和执行类似的操作become
,但 Erlang 的标准库并没有真正考虑到第二个用例。对于现实世界的应用程序,我只能推荐第一种方法,它被目前存在的每个 Erlang 应用程序广泛使用。第二个不常见,可能会导致问题
* *最后一个示例中对函数的调用become/1
可能是?MODULE:become(Pid)
为了避免将来与热代码加载相关的潜在崩溃。*