在討論監(jiān)督與錯誤處理細節(jié)之前,讓我們先一起來看一下 Erlang 進程的終止過程,或者說 Erlang 的術語 exit。
進程執(zhí)行 exit(normal) 結(jié)束或者運行完所有的代碼而結(jié)束都被認為是進程的正常(normal)終止。
進程因為觸發(fā)運行時錯誤(例如,除零、錯誤匹配、調(diào)用不存在了函數(shù)等)而終止被稱之為異常終止。進程執(zhí)行 exit(Reason) (注意此處的 Reason 是除 normal 以外的值)終止也被稱之為異常終止。
一個 Erlang 進程可以與其它 Erlang 進程建立連接。如果一個進程調(diào)用 link(Other_Pid),那么它就在其自己與 Othre_Pid 進程之間創(chuàng)建了一個雙向連接。當一個進程結(jié)束時,它會發(fā)送信號至所有與之有連接的進程。
這個信號攜帶著進程的進程標識符以及進程結(jié)束的原因信息。
進程收到進程正常退出的信號時默認情況下是直接忽略它。
但是,如果進程收到的是異常終止的信號,則默認動作為:
所以,你可以使用連接的方式把同一事務的所有進程連接起來。如果其中一個進程異常終止,事務中所有進程都會被殺死。正是因為在實際生產(chǎn)過程中,常常有創(chuàng)建進程同時與之建立連接的需求,所以存在這樣一個內(nèi)置函數(shù) spawn_link,與 spawn 不同之處在于,它創(chuàng)建一個新進程同時在新進程與創(chuàng)建者之間建立連接。
下面給出了 ping pong 示例子另外一種實現(xiàn)方法,它通過連接終止 "pong" 進程:
-module(tut20).
-export([start/1, ping/2, pong/0]).
ping(N, Pong_Pid) ->
link(Pong_Pid),
ping1(N, Pong_Pid).
ping1(0, _) ->
exit(ping);
ping1(N, Pong_Pid) ->
Pong_Pid ! {ping, self()},
receive
pong ->
io:format("Ping received pong~n", [])
end,
ping1(N - 1, Pong_Pid).
pong() ->
receive
{ping, Ping_PID} ->
io:format("Pong received ping~n", []),
Ping_PID ! pong,
pong()
end.
start(Ping_Node) ->
PongPID = spawn(tut20, pong, []),
spawn(Ping_Node, tut20, ping, [3, PongPID]).
(s1@bill)3> tut20:start(s2@kosken).
Pong received ping
<3820.41.0>
Ping received pong
Pong received ping
Ping received pong
Pong received ping
Ping received pong
與前面的代碼一樣,ping pong 程序的兩個進程仍然都是在 start/1 函數(shù)中創(chuàng)建的,“ping”進程在單獨的結(jié)點上建立的。但是這里做了一些小的改動,用到了內(nèi)置函數(shù) link?!癙ing” 結(jié)束時調(diào)用 exit(ping) ,使得一個終止信號傳遞給 “pong” 進程,從而導致 “pong” 進程終止。
也可以修改進程收到異常終止信號時的默認行為,避免進程被殺死。即,把所有的信號都轉(zhuǎn)變?yōu)橐话愕南⑻砑拥叫盘柦邮者M程的消息隊列中,消息的格式為 {'EXIT',FromPID,Reason}。我們可以通過如下的代碼來設置:
process_flag(trap_exit, true)
還有其它可以用的進程標志,可參閱 erlang (3)。標準用戶程序一般不需要改變進程對于信號的默認處理行為,但是對于 OTP 中的管理程序這個接口還是很有必要的。下面修改了 ping pong 程序來打印輸出進程退出時的信息:
-module(tut21).
-export([start/1, ping/2, pong/0]).
ping(N, Pong_Pid) ->
link(Pong_Pid),
ping1(N, Pong_Pid).
ping1(0, _) ->
exit(ping);
ping1(N, Pong_Pid) ->
Pong_Pid ! {ping, self()},
receive
pong ->
io:format("Ping received pong~n", [])
end,
ping1(N - 1, Pong_Pid).
pong() ->
process_flag(trap_exit, true),
pong1().
pong1() ->
receive
{ping, Ping_PID} ->
io:format("Pong received ping~n", []),
Ping_PID ! pong,
pong1();
{'EXIT', From, Reason} ->
io:format("pong exiting, got ~p~n", [{'EXIT', From, Reason}])
end.
start(Ping_Node) ->
PongPID = spawn(tut21, pong, []),
spawn(Ping_Node, tut21, ping, [3, PongPID]).
(s1@bill)1> tut21:start(s2@gollum).
<3820.39.0>
Pong received ping
Ping received pong
Pong received ping
Ping received pong
Pong received ping
Ping received pong
pong exiting, got {'EXIT',<3820.39.0>,ping}
更多建議: