【问题标题】:If you send a message to a process before giving out its pid, is it guaranteed to recieve that message first?如果您在发出 pid 之前向进程发送消息,是否保证首先收到该消息?
【发布时间】:2013-08-03 20:03:06
【问题描述】:

假设有一个进程 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.

【问题讨论】:

    标签: concurrency erlang


    【解决方案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中,我认为这真的是一篇有趣的论文!

    【讨论】:

    • 为什么你认为同一节点上的进程可以保证?在那篇论文中,答案似乎是“也许”。
    • @Dog 在另一篇名为A Semantics For Distributed Erlang 的论文中,据说消息即时传递。并且m1 保证在m2 之前到达同一节点。这是链接:erlang.org/workshop/2005/ErlangSemantics.pdf
    • 但是我认为我们不应该依赖这个。
    【解决方案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 仿真器和 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 将在m1 之后排队到远程节点。所以A会按照m1、m2的顺序获取消息。

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

    在当前版本的 Erlang 中,崩溃的唯一方法是使 A 和 B 在不同的远程节点上。在这种情况下,m1A 排队到 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

    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}.
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-08-02
      • 1970-01-01
      • 1970-01-01
      • 2021-10-07
      • 1970-01-01
      • 2016-08-09
      相关资源
      最近更新 更多