如果您在发出消息之前发送消息给它的pid,是否保证先接收该消息? [英] If you send a message to a process before giving out its pid, is it guaranteed to recieve that message first?

查看:111
本文介绍了如果您在发出消息之前发送消息给它的pid,是否保证先接收该消息?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设有一个过程 B ,它接收一个pid并发送 m2 。如果你生成 A 并发送 m1 ,然后发送 A B A 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.


解决方案

您的代码无法崩溃,因为进程是本地

  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 。当远程节点将出队消息时,在将 A 写入B的邮箱之前,首先将 m1 写入A的邮箱。 p>

如果过程 A是远程的,B是本地,结果将仍然相同。在第3行,消息 m1 将为远程节点排队,并且在第4行,消息将被写入B,然后在第1行消息 m2 将在 m1 后入队到远程节点。因此A将以m1,m2顺序得到消息。



同样,如果进程 A是本地的,B是远程的,A将得到消息在通过网络发送到B的节点之前,复制到它的邮箱。



对于当前版本的Erlang,这种崩溃的唯一方法是不同远程节点上的A和B 。在这种情况下, m1 被排入A的节点,然后 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 - >
rpc:cast(ANode,erlang,whereis,[user])
end,lists:seq(1,10000)),
A!m1,
!A
end),
寄存器(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 A \\\
)end),
C = rpc:call(node_c @ localhost,erlang,whereis,[process_c]),
C! {process_a,A}。


Say there is a process B, which receives a pid and sends m2 to it. If you spawn A and send it m1, and then send A to B , is A guaranteed to get m1 before m2?

In other words, can this crash?

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

解决方案

Your code cannot crash because all processes are local.

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

When evaluating line 3, both BEAM emulator and HiPE invoke the erl_send built-in function (BIF). Since A is a local process, erl_send (actually do_send) eventually calls erts_send_message which enqueues the message in the mailbox. In SMP mode, the thread actually acquires a lock on the mailbox.

So when evaluating line 4 and sending A to process B, A already has m1 in its mailbox. So m2 can only be enqueued after m1.

Whether this result is particular of the current implementation of Erlang is debatable, even if this is not guaranteed by documentation. Indeed, each process need a mailbox and this mailbox needs to be filled somehow. This is done synchronously on line 3. To do it asynchronously would either require another thread in-between or several mailboxes per process (e.g. one per scheduler to avoid the lock on the mailbox). Yet I do not think this would make sense performance-wise.

If processes A and B were remote but within the same node, the behavior is slightly different but the result would be the same with the current implementation of Erlang. On line 3, message m1 will be enqueued for the remote node and on line 4, message A will be enqueued afterwards. When remote node will dequeue messages, it will first write m1 to A's mailbox before writing A to B's mailbox.

If process A was remote and B was local, the result would still be the same. On line 3, message m1 will be enqueued for the remote node and on line 4, message will be written to B, but then on line 1, message m2 will be enqueued to remote node after m1. So A will get messages in m1, m2 order.

Likewise, if process A was local and B was remote, A will get the message copied to its mailbox on line 3 before anything is sent over the network to B's node.

With the current version of Erlang, the only way for this to crash is to have A and B on distinct remote nodes. In this case, m1 is enqueued to A's node before A is enqueued to B's node. However, delivery of these messages is not synchronous. Delivery to B's node could happen first, for example if many messages are already enqueued for A's node.

The following code (sometimes) triggers the crash by filling queue to A's node with junk messages that slow delivery of 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}.

这篇关于如果您在发出消息之前发送消息给它的pid,是否保证先接收该消息?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆