3

假设有一个进程B,它接收一个 pid 并发送给m2它。如果你spawnA并发送它m1,然后发送AB,是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.
4

2 回答 2

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,它会将邮箱中的消息排入队列。在 SMP 模式下,线程实际上获取了邮箱上的锁。

因此,当评估第 4 行并将 A 发送到进程 B 时,A 的邮箱中已经有 m1。所以m2只能入队后m1

这个结果是否特定于 Erlang 的当前实现是有争议的,即使文档不能保证这一点。事实上,每个进程都需要一个邮箱,并且这个邮箱需要以某种方式填充。这是在第 3 行同步完成的。要异步完成,要么需要另一个线程,要么每个进程需要多个邮箱(例如,每个调度程序一个,以避免邮箱锁定)。然而,我认为这在性能方面没有意义。

如果进程A 和 B 是远程的但在同一个节点中,则行为略有不同,但结果与 Erlang 的当前实现相同。在第 3 行,m1远程节点的消息将入队,在第 4 行,消息A将随后入队。当远程节点将消息出列时,它会先写入m1A 的邮箱,然后再写入AB 的邮箱。

如果进程A 是 remote 而 B 是 local,结果仍然是相同的。在第 3 行,消息m1将被排队到远程节点,在第 4 行,消息将被写入 B,但是在第 1 行,消息m2将在之后排队到远程节点m1。所以 A 将以 m1, m2 的顺序获取消息。

同样,如果进程A 是本地进程 B 是远程进程,则A 将在第 3 行将消息复制到其邮箱,然后再通过网络将任何内容发送到 B 的节点。

对于当前版本的 Erlang,崩溃的唯一方法是将A 和 B 放在不同的远程节点上。在这种情况下,m1先入队到 A 的节点,然后A再入队到 B 的节点。但是,这些消息的传递不是同步的。例如,如果许多消息已经为 A 的节点排队,则可能首先发送到 B 的节点。

以下代码(有时)通过将队列填充到 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

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}.
于 2013-08-05T12:50:09.177 回答
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中,我认为这真的是一篇有趣的论文!

于 2013-08-02T18:54:37.233 回答