假设有一个进程B
,它会收到一个pid并向其发送m2
。如果您生成A
并发送m1
,然后将A
发送到B
,则A
保证在m1
之前获得m2
}}?
换句话说,这会崩溃吗?
-module(test).
-compile(export_all).
test() ->
B = spawn_link(fun() -> receive P -> P ! m2 end end),
A = spawn_link(fun() -> receive X -> X=m1 end end),
A ! m1,
B ! A.
答案 0 :(得分:4)
您的代码无法崩溃,因为所有流程都是本地的。
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
将在之后排队。当远程节点将消息出列时,它会先将m1
写入A的邮箱,然后再将A
写入B的邮箱。
如果流程 A是远程且B是本地,则结果仍然相同。在第3行,消息m1
将为远程节点排队,在第4行,消息将写入B,但在第1行,消息m2
将在{{{{}}之后排队到远程节点1}}。因此A将以m1,m2顺序获取消息。
同样,如果进程 A是本地的且B是远程的,则在通过网络向B的节点发送任何内容之前,A将将消息复制到第3行的邮箱。
使用当前版本的Erlang,崩溃的唯一方法是在不同的远程节点上使用 A和B 。在这种情况下,m1
在m1
入队到B的节点之前被排队到A的节点。但是,这些消息的传递不是同步的。首先可以发送到B的节点,例如,如果许多消息已经为A的节点排队。
以下代码(有时)通过使用减慢A
传递的垃圾邮件将队列填充到A的节点来触发崩溃。
m1
$ erl -sname node_c@localhost
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 node_b@localhost
B = spawn_link(fun() -> receive P -> P ! m2 end end),
C = rpc:call(node_c@localhost, erlang, whereis, [process_c]),
C ! {process_b, B}.
$ erl -sname node_a@localhost
答案 1 :(得分:1)
如果这两个进程在同一节点上都 ,那么确保A在m2之前得到m1。
但是当两个进程在不同节点上时,则无法保证。
有关此问题的论文Programming Distributed Erlang Applications: Pitfalls and Recipes
。
以下是链接:http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.116.9929&rep=rep1&type=pdf
你的问题在本文的 2.2 中,我认为这真是一篇有趣的论文!