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
之前到达。这是一个链接:但我认为我们不应该依赖于此。