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