Concurrency 如果在给出进程的pid之前向进程发送消息,是否保证首先接收该消息?
假设有一个进程Concurrency 如果在给出进程的pid之前向进程发送消息,是否保证首先接收该消息?,concurrency,erlang,Concurrency,Erlang,假设有一个进程B,它接收一个pid并向其发送m2。如果生成A并将其发送到m1,然后将A发送到B,那么A是否保证在m2之前获得m1 换句话说,这会崩溃吗 -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
B
,它接收一个pid并向其发送m2
。如果生成A
并将其发送到m1
,然后将A
发送到B
,那么A
是否保证在m2
之前获得m1
换句话说,这会崩溃吗
-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.
如果两个进程都在同一个节点上,则A确实保证在m2之前获得m1 但是,当两个进程在不同的节点上时,就不能保证 关于这个问题,有一篇论文<代码>编程分布式Erlang应用程序:陷阱和诀窍 以下是链接:
你的问题在这篇论文的2.2中,我认为这真是一篇有趣的论文 您的代码不能崩溃,因为所有进程都是本地进程
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 emulator和HiPE都会调用内置函数(BIF)。由于A是一个本地进程,erl_send(实际上)最终会调用邮箱中的消息。在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
将在m1
之后排队等待远程节点。所以A将得到m1,m2顺序的消息
同样,如果进程A是本地的,而B是远程的,则在通过网络向B的节点发送任何消息之前,A会将消息复制到第3行的邮箱中
对于当前版本的Erlang,崩溃的唯一方法是在不同的远程节点上有A和B。在这种情况下,m1
在A
进入B的节点之前进入A的节点。但是,这些消息的传递不是同步的。传递到B的节点可能首先发生,例如,如果许多消息已经排队等待A的节点
以下代码(有时)通过向A的节点填充垃圾消息来触发崩溃,这些垃圾消息会减慢m1
的传递
$erl-sname节点_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).
B = spawn_link(fun() -> receive P -> P ! m2 end end),
C = rpc:call(node_c@localhost, erlang, whereis, [process_c]),
C ! {process_b, B}.
A = spawn_link(fun() -> receive X -> X = m1 end, io:format("end of A\n") end),
C = rpc:call(node_c@localhost, erlang, whereis, [process_c]),
C ! {process_a, A}.
$erl-sname节点_b@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).
B = spawn_link(fun() -> receive P -> P ! m2 end end),
C = rpc:call(node_c@localhost, erlang, whereis, [process_c]),
C ! {process_b, B}.
A = spawn_link(fun() -> receive X -> X = m1 end, io:format("end of A\n") end),
C = rpc:call(node_c@localhost, erlang, whereis, [process_c]),
C ! {process_a, A}.
$erl-sname节点_a@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).
B = spawn_link(fun() -> receive P -> P ! m2 end end),
C = rpc:call(node_c@localhost, erlang, whereis, [process_c]),
C ! {process_b, B}.
A = spawn_link(fun() -> receive X -> X = m1 end, io:format("end of A\n") end),
C = rpc:call(node_c@localhost, erlang, whereis, [process_c]),
C ! {process_a, A}.
为什么您认为对于同一节点上的进程来说这是有保证的?在那篇论文中,答案似乎是“可能”。@Dog在另一篇名为《分布式Erlang的语义》的论文中,据说消息是即时传递的。并且
m1
保证在同一节点上的m2
之前到达。这是一个链接:但我认为我们不应该依赖于此。