第9章 併發編程中的錯誤處理
1. 鏈接:鏈接定義了一種在兩個進程之間的傳播路徑。如果兩個進程被鏈接在一起,如果其中一個進程消亡,那麼系統就會向另一個進程發送一個退出信號。我們把一羣與某個給定的進程進行外國投資的進程集合稱爲該進程的鏈接集。
鏈接通過在一個進程中調用link(Pid)來完成,Pid是另一個進程的ID。
2. 退出信號:當一個進程消亡時,它會產生一個叫做退出信號的東西。系統會向這個進程的所有鏈接進程發送這個退出信號。
退出信號不是一個消息,它是一個信號。退出信號包含一個參數來描述進程消亡的原因,可以通過調用exit(Reason),也可以與系統自動設置。
當一個進程成功的完成spawn所指定的函數而退出時,Why就是normal。
3. 系統進程:當一個進程接收到一個非正常的退出信號時它自己也會消亡,除非它是那種特殊類型的進程-系統進程。當Pid進程向一個系統進程發送一個內容Why的退出信號時,系統會把退出信號轉換爲消息{'EXIT', Pid, Why},然後送入到系統進程郵箱。
可以通過在一個進程中調用process_flag(trap_exit, true)來將這個進程轉換爲一個系統進程。
4. 裝死:在進程Pid1中調用exit(Pid2,X),這時Pid1會向Pid2發送一個退出信號,但Pid1並沒有退出。
5. 進程收到退出信號後的處理關係:
是否捕獲 收到的退出信號 收到信號後的動作
是否是系統進程)
true kill 消亡,向鏈接集廣播退出信號killed
true X 將{'EXIT', Pid, X}消息加入到郵箱
false normal 繼續運行,不做任何事
false kill 消亡,向鏈接集廣播退出信號killed
false X 消亡,向鏈接集廣播退出信號X
6. kill退出信號可以殺死任何進程,而不是向它發送消息。
exit(Pid, kill)會向進程Pid發送kill退出信號
而exit(kill)只會向鏈接進程發送原因爲kill的退出消息,而不是kill退出信號。
7. on_exit處理程序
on_exit(Pid, Fun) ->
spawn(fun() ->
process_flag(trap_exit, true),
link(Pid),
receive
{'EXIT',Pid,Why} -> Fun(Why)
end
end).
8. 捕獲退出的編程模式
模式一:我不在乎創建的進程是否崩潰
創建一個並行進程,當被生成的進程崩潰時,當前進程不會察覺
Pid = spawn(Fun).
模式二:如果我創建的進程非正常的崩潰,我也消亡
%%不要在這之前設置退出信號捕獲狀態
Pid = spawn_link(Fun).
模式三:如果我創建的進程崩潰,我需要處理錯誤
...
process_flag(trap_exit, true),
Pid = spawn_link(Fun),
...
loop(...).
loop(State) ->
receive
{'EXIT', SomePid, Reason} ->
%% do somethin with the error
loop(State);
...
end
9. erlang:is_process_alive(Pid) -> true|false,查看進程Pid是否還活着。
10. 錯誤處理原語
@spec spawn_link(Fun) -> Pid %% 創建進程並鏈接它們
@spec process_flag(trap_exit, true) %% 把一個普通進程轉換爲系統進程,可以再把標誌設爲false而取消對退出信號的處理
@spec link(Pid) -> true %% link是對稱的。如果Pid不在在,會拋出noproc異常,如果已經鏈接,系統會忽略這個調用(不會返回false)
@spec unlink(Pid) -> true %% 移除先前的link
@spec exit(Why) -> none() %% 如果不在catch之內的話,會向當前進程的鏈接集廣播退出信號
@spec exit(Pid, Why) -> true
@spec erlang:monitor(process, Item) -> MonitorRef %% 建立一個監視器,其中Item是一個Pid或者是一個註冊的原子
監視器是非對稱的,A監視B時,B退出時,A會收到退出信號,而A退出時,B不會收到退出信號。
11. 存活進程:消亡後會被自動重啓
keep_alive(Name, Fun) ->
register(Name, Pid=spawn(Fun)),
on_exit(Pid, fun(_Why) -> keep_alive(Name, Fun) end).
這個代碼中存在競爭性條件,有問題。