Erlang 錯(cuò)誤處理

2022-07-07 15:43 更新

Erlang錯(cuò)誤處理

在討論監(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í),它會發(fā)送信號至所有與之有連接的進(jìn)程。

這個(gè)信號攜帶著進(jìn)程的進(jìn)程標(biāo)識符以及進(jìn)程結(jié)束的原因信息。

進(jìn)程收到進(jìn)程正常退出的信號時(shí)默認(rèn)情況下是直接忽略它。

但是,如果進(jìn)程收到的是異常終止的信號,則默認(rèn)動(dòng)作為:

  • 接收到異常終止信號的進(jìn)程忽略消息隊(duì)列中的所有消息
  • 殺死自己
  • 將相同的錯(cuò)誤消息傳遞給連接到它的所有進(jìn)程。

所以,你可以使用連接的方式把同一事務(wù)的所有進(jìn)程連接起來。如果其中一個(gè)進(jìn)程異常終止,事務(wù)中所有進(jìn)程都會被殺死。正是因?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è)終止信號傳遞給 “pong” 進(jìn)程,從而導(dǎo)致 “pong” 進(jìn)程終止。

也可以修改進(jìn)程收到異常終止信號時(shí)的默認(rèn)行為,避免進(jìn)程被殺死。即,把所有的信號都轉(zhuǎn)變?yōu)橐话愕南⑻砑拥叫盘柦邮者M(jìn)程的消息隊(duì)列中,消息的格式為 {'EXIT',FromPID,Reason}。我們可以通過如下的代碼來設(shè)置:

process_flag(trap_exit, true)

還有其它可以用的進(jìn)程標(biāo)志,可參閱 erlang (3)。標(biāo)準(zhǔn)用戶程序一般不需要改變進(jìn)程對于信號的默認(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}
以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號