<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    莊周夢蝶

    生活、程序、未來
       :: 首頁 ::  ::  :: 聚合  :: 管理

    Erlang入門(四)——錯誤處理和魯棒性

    Posted on 2007-06-25 17:10 dennis 閱讀(6106) 評論(0)  編輯  收藏 所屬分類: erlang
        去了趟福州,事情沒搞定,托給同學幫忙處理了,回家休息了兩天就來上班了。回家這幾天最大的收獲是第四次重讀《深入Java虛擬機》,以前不大明了的章節豁然開朗,有種開竅的感覺,水到渠成,看來技術的學習還是急不來。
        閑話不提,繼續Erlang的學習,上次學習到分布式編程的章節,剩下三章分別是錯誤處理、構造健壯的系統和雜項,錯誤處理和構造健壯的系統今天一起讀了,僅摘記下。
        任何一門語言都有自己的錯誤處理機制,Erlang也不例外,語法錯誤編譯器可以幫你指出,而邏輯錯誤和運行時錯誤就只有靠程序員利用Erlang提供的機制來妥善處理,放置程序的崩潰。
        Erlang的機制有:
    1)監控某個表達式的執行
    2)監控其他進程的行為
    3)捕捉未定義函數執行錯誤等

    一、catch和throw語句
        調用某個會產生錯誤的表達式會導致調用進程的非正常退出,比如錯誤的模式匹配(2=3),這種情況下可以用catch語句:
    catch expression
        試看一個例子,一個函數foo:
    foo(1->
    hello;
    foo(
    2->
    throw({myerror
    , abc});
    foo(
    3->
    tuple_to_list(a);
    foo(
    4->
    exit({myExit, 222}).

    當沒有使用catch的時候,假設有一個標識符為Pid的進程調用函數foo(在一個模塊中),那么:
    foo(1) - 返回hello
    foo(2) - 語句throw({myerror, abc})執行,因為我們沒有在一個catch中調用foo(2),因此進程Pid將因為錯誤而終止。

    foo(3) - tuple_to_list將一個元組轉化為列表,因為a不是元組,因此進程Pid同樣因為錯誤而終止

    foo(4) - 因為沒有使用catch,因此foo(4)調用了exit函數將使進程Pid終止,{myExit, 222} 參數用于說明退出的原因。

    foo(5) - 進程Pid將因為foo(5)的調用而終止,因為沒有和foo(5)匹配的函數foo/1。

        讓我們看看用catch之后是什么樣:
    demo(X) ->
    case catch foo(X) of
      {myerror
    , Args} ->
           {user_error
    , Args};
      {
    'EXIT', What} ->
           {caught_error
    , What};
      Other 
    ->
           Other
    end
    .
    再看看結果,
    demo(1) - 沒有錯誤發生,因此catch語句將返回表達式結果hello
    demo(2) - foo(2)拋出錯誤{myerror, abc},被catch返回,因此將返回{user_error,abc}

    demo(3) - foo(3)執行失敗,因為參數錯誤,因此catch返回{'EXIT',badarg'},最后返回{caught_error,badarg}

    demo(4) - 返回{caught_error,{myexit,222}}
    demo(5) - 返回{caught_error,function_clause}

        使用catch和throw可以將可能產生錯誤的代碼包裝起來,throw可以用于尾遞歸的退出等等。Erlang是和scheme一樣進行尾遞歸優化的,它們都沒有顯式的迭代結構(比如for循環)

    二、進程的終止
        在進程中調用exit的BIFs就可以顯式地終止進程,exit(normal)表示正常終止,exit(Reason)通過Reason給出非正常終止的原因。進程的終止也完全有可能是因為運行時錯誤引起的。

    三、連接的進程
        進程之間的連接是雙向的,也就是說進程A打開一個連接到B,也意味著有一個從B到A的連接。當進程終止的時候,有一個EXIT信號將發給所有與它連接的進程。信號的格式如下:
                   {'EXIT', Exiting_Process_Id, Reason}
    Exiting_Process_Id 是指終止的進程標記符
    Reason 是進程終止的原因。如果Reason是normal,接受這個信號的進程的默認行為是忽略這個信號。默認對Exit信號的處理可以被重寫,以允許進程對Exit信號的接受做出不同的反應。
    1.連接進程:
    通過link(Pid),就可以在調用進程與進程Pid之間建立連接
    2.取消連接
    反之通過unlink(Pid)取消連接。
    3.創立進程并連接:
    通過spawn_link(Module, Function, ArgumentList)創建進程并連接,該方法返回新創建的進程Pid

        通過進程的相互連接,許多的進程可以組織成一個網狀結構,EXIT信號(非normal)從某個進程發出(該進程終止),所有與它相連的進程以及與這些進程相連的其他進程,都將收到這個信號并終止,除非它們實現了自定義的EXIT信號處理方法。一個進程鏈狀結構的例子:
    -module(normal).
    -export([start/1, p1/1, test/1]).
    start(N) 
    ->
    register(start
    , spawn_link(normal, p1, [N - 1])).
     p1(
    0->
       top1();
     p1(N) 
    ->
       top(spawn_link(normal
    , p1, [N - 1]),N).
    top(
    Next, N) ->
    receive
    ->
    Next ! X,
    io
    :format("Process ~w received ~w~n", [N,X]),
    top(
    Next,N)
    end
    .
    top1() 
    ->
    receive
    stop 
    ->
    io
    :format("Last process now exiting ~n", []),
    exit(finished);
    ->
    io
    :format("Last process received ~w~n", [X]),
    top1()
    end
    .
    test(Mess) 
    ->
    start 
    ! Mess.

    執行:
    > normal:start(3).
    true
    > normal:test(123).
    Process 
    2 received 123
    Process 
    1 received 123
    Last process received 123

    > normal:test(stop).
    Process 
    2 received stop
    Process 
    1 received stop
    Last process now exiting
    stop

    四、運行時失敗
        一個運行時錯誤將導致進程的非正常終止,伴隨著非正常終止EXIT信號將發出給所有連接的進程,EXIT信號中有Reason并且Reason中包含一個atom類型用于說明錯誤的原因,常見的原因如下:

    badmatch - 匹配失敗,比如一個進程進行1=3的匹配,這個進程將終止,并發出{'EXIT', From, badmatch}信號給連接的進程

    badarg  - 顧名思義,參數錯誤,比如atom_to_list(123),數字不是atom,因此將發出{'EXIT', From, badarg}信號給連接進程

    case_clause - 缺少分支匹配,比如
       
    = 3,
    case M of
      1 ->
        yes;
      2 ->
        no
    end
    .
    沒有分支3,因此將發出{'EXIT', From, case_clause}給連接進程

    if_clause - 同理,if語句缺少匹配分支

    function_clause - 缺少匹配的函數,比如:
    foo(1->
      yes;
    foo(
    2->
      
    no.
    如果我們調用foo(3),因為沒有匹配的函數,將發出{'EXIT', From, function_clause} 給連接的進程。

    undef - 進程執行一個不存在的函數

    badarith - 非法的算術運算,比如1+foo。

    timeout_value - 非法的超時時間設置,必須是整數或者infinity

    nocatch - 使用了throw,沒有相應的catch去通訊。

    五、修改默認的信號接收action
       當進程接收到EXIT信號,你可以通過process_flag/2方法來修改默認的接收行為。執行process_flag(trap_exit,true)設置捕獲EXIT信號為真來改變默認行為,也就是將EXIT信號作為一般的進程間通信的信號進行接受并處理;process_flag(trap_exit,false)將重新開啟默認行為。
       例子:
    -module(link_demo).
    -export([start/0, demo/0, demonstrate_normal/0, demonstrate_exit/1,
    demonstrate_error
    /0, demonstrate_message/1]).
    start() 
    ->
      register(demo
    , spawn(link_demo, demo, [])).
    demo() 
    ->
      process_flag(trap_exit
    , true),
    demo1()
    .
      demo1() 
    ->
      receive
        {
    'EXIT', From, normal} ->
          io
    :format("Demo process received normal exit from ~w~n",[From]),
         demo1();
        {
    'EXIT', From, Reason} ->
          io
    :format("Demo process received exit signal ~w from ~w~n",[Reason, From]),
         demo1();
        finished_demo 
    ->
          io
    :format("Demo finished ~n", []);
        Other 
    ->
          io
    :format("Demo process message ~w~n", [Other]),
         demo1()
      end
    .
    demonstrate_normal() 
    ->
      
    link(whereis(demo)).
    demonstrate_exit(What) 
    ->
      
    link(whereis(demo)),
      
    exit(What).
    demonstrate_message(What) 
    ->
      demo 
    ! What.
    demonstrate_error() 
    ->
      
    link(whereis(demo)),
      
    1 = 2.
     
        創建的進程執行demo方法,demo方法中設置了trap_exit為true,因此,在receive中可以像對待一般的信息一樣處理EXIT信號,這個程序是很簡單了,測試看看:
    > link_demo:start().
    true
    > link_demo:demonstrate_normal().
    true
    Demo process received normal 
    exit from <0.13.1>
    > link_demo:demonstrate_exit(hello).
    Demo process received 
    exit signal hello from <0.14.1>
    ** exited: hello **

    > link_demo:demonstrate_exit(normal).
    Demo process received normal 
    exit from <0.13.1>
    ** exited: normal **

    > link_demo:demonstrate_error().
    !!! Error in process <0.17.1> in function
    !!! link_demo:demonstrate_error()
    !!! reason badmatch
    ** exited: badmatch **
    Demo process received 
    exit signal badmatch from <0.17.1>

    六、未定義函數和未注冊名字
    1.當調用一個未定義的函數時,Mod:Func(Arg0,...,ArgN),這個調用將被轉為:
    error_handler:undefined_function(Mod, Func, [Arg0,...,ArgN])
    其中的error_handler模塊是系統自帶的錯誤處理模塊

    2.當給一個未注冊的進程名發送消息時,調用將被轉為:
    error_handler:unregistered_name(Name,Pid,Message)

    3.如果不使用系統自帶的error_handler,可以通過process_flag(error_handler, MyMod) 設置自己的錯誤處理模塊。

    七、Catch Vs. Trapping Exits
    這兩者的區別在于應用場景不同,Trapping Exits應用于當接收到其他進程發送的EXIT信號時,而catch僅用于表達式的執行。

    第8章介紹了如何利用錯誤處理機制去構造一個健壯的系統,用了幾個例子,我將8.2節的例子完整寫了下,并添加客戶端進程用于測試:
    -module(allocator).
    -export([start/1,server/2,allocate/0,free/1,start_client/0,loop/0]).
    start(Resources) 
    ->
       Pid 
    = spawn(allocator, server, [Resources,[]]),
    register(resource_alloc
    , Pid).
    %函數接口
    allocate() 
    ->
       request(alloc)
    .
    free(Resource) 
    ->
      request({free
    ,Resource}).
    request(Request) 
    ->
      resource_alloc 
    ! {self(),Request},
      receive
        {resource_alloc
    , error} ->
          
    exit(bad_allocation); % exit added here
        {resource_alloc
    , Reply} ->
          Reply
     end
    .
    % The server.
    server(Free
    , Allocated) ->
     process_flag(trap_exit
    , true),
     receive
       {From
    ,alloc} ->
             allocate(Free
    , Allocated, From);
       {From
    ,{free,R}} ->
            free(Free
    , Allocated, From, R);
       {
    'EXIT', From, _ } ->
           check(Free
    , Allocated, From)
     end
    .
    allocate([R
    |Free], Allocated, From) ->
       
    link(From),
       io
    :format("連接客戶端進程~w~n",[From]),
       From 
    ! {resource_alloc,{yes,R}},
       server(Free
    , [{R,From}|Allocated]);
    allocate([]
    , Allocated, From) ->
       From 
    ! {resource_alloc,no},
       server([]
    , Allocated).
    free(Free
    , Allocated, From, R) ->
      case lists
    :member({R,From}, Allocated) of
       true 
    ->
                  From 
    ! {resource_alloc,ok},
                  Allocated1 
    = lists:delete({R, From}, Allocated),
                  case lists
    :keysearch(From,2,Allocated1) of
                         false
    ->
                                
    unlink(From),
                            io
    :format("從進程~w斷開~n",[From]);
                         _
    ->
                                true
                  end
    ,
                 server([R
    |Free],Allocated1);
       false 
    ->
               From 
    ! {resource_alloc,error},
             server(Free
    , Allocated)
     end
    .

    check(Free
    , Allocated, From) ->
       case lists
    :keysearch(From, 2, Allocated) of
             false 
    ->
               server(Free
    , Allocated);
            {value
    , {R, From}} ->
               check([R
    |Free],
               lists
    :delete({R, From}, Allocated), From)
    end
    .
    start_client()
    ->
        Pid2
    =spawn(allocator,loop,[]),
        register(client
    , Pid2).
    loop()
    ->
        receive
            allocate
    ->
                allocate()
    ,
                loop();
            {free
    ,Resource}->
                free(Resource)
    ,
                loop();
            stop
    ->
                true;
            _
    ->
                loop()
        end
    .
        

    回家了,有空再詳細說明下這個例子吧。執行:
    1> c(allocator).
    {ok
    ,allocator}
    2> allocator:start([1,2,3,4,5,6]).
    true
    3> allocator:start_client().
    true
    4> client!allocate
    .
    allocate連接客戶端進程
    <0.37.0>

    5> client!allocate.
    allocate連接客戶端進程
    <0.37.0>

    6> client!allocate.
    allocate連接客戶端進程
    <0.37.0>

    7> allocator:allocate().
    連接客戶端進程
    <0.28.0>
    {yes
    ,4}
    8> client!{free,1}.
    {free
    ,1}
    9> client!{free,2}.
    {free
    ,2}
    10> client!allocate.
    allocate連接客戶端進程
    <0.37.0>

    11> client!allocate.
    allocate連接客戶端進程
    <0.37.0>

    12> client!stop.
    stop
    13> allocator:allocate().
    連接客戶端進程
    <0.28.0>
    {yes
    ,3}
    14> allocator:allocate().
    連接客戶端進程
    <0.28.0>
    {yes
    ,2}
    15> allocator:allocate().
    連接客戶端進程
    <0.28.0>
    {yes
    ,1}
    16>






    主站蜘蛛池模板: 国产福利免费观看| 色爽黄1000部免费软件下载| 亚洲一区二区女搞男| 暖暖免费高清日本一区二区三区| 久爱免费观看在线网站| 黄色大片免费网站| 亚洲日韩看片无码电影| 亚洲精品一区二区三区四区乱码| 国产成人亚洲综合| 四虎影视在线永久免费看黄| 免费无码肉片在线观看| 免费专区丝袜脚调教视频| 日韩成人免费视频| 国精产品一区一区三区免费视频 | 最近免费最新高清中文字幕韩国| 一个人免费播放在线视频看片 | 在线观着免费观看国产黄| 全免费毛片在线播放| 亚洲一级免费视频| 最刺激黄a大片免费网站| 日韩免费电影网址| 国产免费AV片在线观看| 野花香在线视频免费观看大全| xxxxx做受大片在线观看免费| 日日摸夜夜添夜夜免费视频| 精品在线观看免费| 爱情岛论坛免费视频| 欧洲乱码伦视频免费国产| 日本中文字幕免费看| a一级毛片免费高清在线| 亚洲免费在线观看| 中文字幕一区二区免费| 三年片在线观看免费西瓜视频| 日韩a级无码免费视频| 久久久精品免费国产四虎| 日韩精品免费视频| 国产高清不卡免费在线| 免费看韩国黄a片在线观看| 日韩免费毛片视频| 亚洲国产精品一区二区九九 | 一个人看的www在线免费视频|