您的代碼無法崩潰,因爲所有進程都是本地的。
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
將在之後被排隊。當遠程節點將消息出隊時,在將A
寫入B的郵箱之前,它首先將m1
寫入A的郵箱。
如果過程A是遠程的,B是本地的,結果仍然是一樣的。在第3行,消息m1
將被排入遠程節點,並且在第4行,消息將被寫入B,但是隨後在第1行,消息m2
將在m1
之後被排隊到遠程節點。所以A會以m1,m2的順序得到消息。
同樣,如果進程A是本地的,而B是遠程的,則A會在第3行將消息複製到第3行的郵箱中,然後通過網絡將任何內容發送到B的節點。
在當前版本的Erlang中,導致崩潰的唯一方法是在不同的遠程節點上使用A和B.在這種情況下,前A
排入到B的節點m1
排入到A的節點。但是,這些消息的傳遞不同步。例如,如果許多消息已經排隊等待A的節點,則可以首先發送到B的節點。
下面的代碼(有時)通過填充隊列到A的節點與垃圾消息的m1
交貨慢觸發崩潰。
$ erl -sname [email protected]
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 [email protected]
B = spawn_link(fun() -> receive P -> P ! m2 end end),
C = rpc:call([email protected], erlang, whereis, [process_c]),
C ! {process_b, B}.
$ erl -sname [email protected]
A = spawn_link(fun() -> receive X -> X = m1 end, io:format("end of A\n") end),
C = rpc:call([email protected], erlang, whereis, [process_c]),
C ! {process_a, A}.
爲什麼你認爲它是保證在同一個節點上的進程?在那篇文章中,答案似乎是「可能」。 – Dog
@Dog在另一篇名爲「分佈式Erlang的語義」的文章中,據說_Messages是即時傳遞的。 'm1'保證在'm2'之前到達同一個節點。這裏是鏈接:http://www.erlang.org/workshop/2005/ErlangSemantics.pdf – Taotaotheripper
但是我們不應該依賴於這一點,我想。 – Taotaotheripper