您的代码无法崩溃,因为所有进程都是本地的。
B = spawn_link(fun() -> receive P -> P ! m2 end end), % 1
A = spawn_link(fun() -> receive X -> X=m1 end end), % 2
A ! m1, % 3
B ! A. % 4
当评估线3,既BEAM仿真器和HIPE调用erl_send内置函数(BIF)。由于A是本地进程,因此erl_send(实际上是do_send)最终会调用erts_send_message其中enqueues邮箱中的邮件。在SMP模式下,线程实际上获得了邮箱的锁定。
因此,在评估第4行并将A发送给进程B时,A的邮箱中已经有m1。所以m2
只能在m1
后排队。
这个结果是否与Erlang的当前实现特别有关是有争议的,即使这不是由文档保证的。的确,每个进程都需要一个邮箱,并且需要以某种方式填写该邮箱。这是在第3行同步完成的。要异步执行,每个进程需要另一个线程或多个邮箱(例如,每个调度程序一个邮箱以避免锁定邮箱)。但我认为这不会在表现方面有意义。
如果进程A和B是远程的,但在同一节点内,行为稍有不同,但结果与当前的Erlang实现相同。在第3行,消息m1
将被排入远程节点,并且在第4行,消息A
将在之后被排队。当远程节点将消息出队时,在将A
写入B的邮箱之前,它首先将m1
写入A的邮箱。
如果过程A是远程的,B是本地的,结果仍然是一样的。在第3行,消息m1
将被排入远程节点,并且在第4行,消息将被写入B,但是随后在第1行,消息m2
将在m1
之后被排队到远程节点。所以A会以m1,m2的顺序得到消息。
同样,如果进程A是本地的,而B是远程的,则A会在第3行将消息复制到第3行的邮箱中,然后通过网络将任何内容发送到B的节点。
在当前版本的Erlang中,导致崩溃的唯一方法是在不同的远程节点上使用A和B.在这种情况下,前A
排入到B的节点m1
排入到A的节点。但是,这些消息的传递不同步。例如,如果许多消息已经排队等待A的节点,则可以首先发送到B的节点。
下面的代码(有时)通过填充队列到A的节点与垃圾消息的m1
交货慢触发崩溃。
$ erl -sname [email protected]
C = spawn_link(fun() ->
A = receive {process_a, APid} -> APid end,
B = receive {process_b, BPid} -> BPid end,
ANode = node(A),
lists:foreach(fun(_) ->
rpc:cast(ANode, erlang, whereis, [user])
end, lists:seq(1, 10000)),
A ! m1,
B ! A
end),
register(process_c, C).
$ erl -sname [email protected]
B = spawn_link(fun() -> receive P -> P ! m2 end end),
C = rpc:call([email protected], erlang, whereis, [process_c]),
C ! {process_b, B}.
$ erl -sname [email protected]
A = spawn_link(fun() -> receive X -> X = m1 end, io:format("end of A\n") end),
C = rpc:call([email protected], erlang, whereis, [process_c]),
C ! {process_a, A}.
为什么你认为它是保证在同一个节点上的进程?在那篇文章中,答案似乎是“可能”。 – Dog
@Dog在另一篇名为“分布式Erlang的语义”的文章中,据说_Messages是即时传递的。 'm1'保证在'm2'之前到达同一个节点。这里是链接:http://www.erlang.org/workshop/2005/ErlangSemantics.pdf – Taotaotheripper
但是我们不应该依赖于这一点,我想。 – Taotaotheripper