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