zoukankan      html  css  js  c++  java
  • [Erlang37]error/1 exit/1 exit/2 throw/1的区别

    1. error/1

    主要是系统用来定义内部错误的: Erlang内建的run time error 一共有10种:

    function_clause/case_clause/if_clause/badmatch/badarg/undef/badarith/badfun/badarity/system_limit, 比如:         

     1> erlang:binary_to_list(1).
        ** exception error: bad argument
           in function  binary_to_list/1
           called as binary_to_list(1)

     这上面就是触发了error/1,我们也可以手动触发一下。

     2> erlang:error(badarg).    
        ** exception error: bad argument        

    注意到erlang直接把badarg这种内建的error转成更详细的bad argument

    更进一步,我们也可以使用error/1定义自己的错误

    3> erlang:error("this is my own error").
       ** exception error: "this is my own error"

    这一次,自定义的错就没有被erlang shell认出来。

    2. exit/1 exit/2

    exit有internal exits 和 external exits的区别,我们可以使用exit(Pid,Reason)让别一个进程退出。

    exit/1和error/1非常相似,很多时候可以通用,便是exit语境是退出,更适合于进程退出的情况,还有一个区别就是

    exit/1不会带调用的stack trace信息(方便让其它进程退出时不用带非常大的调用信息,更轻量)。但是error/1会带。

    4> catch exit(test).
      {'EXIT',test}
    5> catch error(test).
       {'EXIT',{test,[{erl_eval,do_apply,6,
               [{file,"erl_eval.erl"},{line,674}]},
                {erl_eval,expr,5,[{file,"erl_eval.erl"},{line,431}]},
                {shell,exprs,7,[{file,"shell.erl"},{line,686}]},
                {shell,eval_exprs,7,[{file,"shell.erl"},{line,641}]},
                {shell,eval_loop,3,[{file,"shell.erl"},{line,626}]}]}} 

    3. throw/1

    throw/1 它最常用配合 try...of catch 处理嵌套case(可以快速跳出),它所携带的信息最少(比exit/1还少一个'EXIT'):

    6> catch throw(2+2).
    4
    7> catch 2+2
    4     

    上面2个case用的catch,都区分不出结果是throw出来的,还是正常计算得到的结果,所以这也是推荐使用try .. of catch的原因:

    8> try throw(2+2) of
    8> V -> {ok, V}
    8> catch
    8>  throw:V -> {error, V}
    8> end.
    {error,4} 

    4. 总结

    进程退出使用exit/1或exit/2, 想快速跳出recursion或快速跳回Top-Level函数时用throw/1,尽量不要使用error/1,

    如果需要得到调用的stack trace信息,可以自己显式的调用erlang:get_stacktrace().得到当前进程最新一次Exception时的的stacktrace。

     

    5. 扩展(gen_server中的几种退出进程方法比较)

     我们在gen_server中安全有5种退出方法,
    5.1. 在init不成功时返回{stop, Reason} 退出;
    5.2. 处理消息出错,或正常退出时返回{stop, Reason, NewState});
    5.3. 使用exit直接退出进程;
    5.4. 使用throw退出进程;
    5.5. 使用error退出进程(由于我们前面讲了error主要用于定义内部错误,所以不推荐使用)。

    前两种是显而易见的,关键是后三种方式退出与前者的区别。
    先来理一理{stop, Reason, NewState},直接exit, 直接throw三者的区别。
    try_dispatch(Mod, Func, Msg, State) ->
        try
        {ok, Mod:Func(Msg, State)}
        catch
        throw:R ->
            {ok, R};
        error:R ->
            Stacktrace = erlang:get_stacktrace(),
            {'EXIT', {R, Stacktrace}, {R, Stacktrace}};
        exit:R ->
            Stacktrace = erlang:get_stacktrace(),
            {'EXIT', R, {R, Stacktrace}}
        end.

    使用stop就是正常退出,不带stack trace信息(他本来就没有crash,也没有stack信息)

    使用exit退出,它带了stack trace信息。

    使用throw退出,它没有带stack trace信息,且要throw出来的term要是符合gen_server标准,比如
    {noreply, NewState}
    {reply, ok, NewState}
    {stop, normal, NewState}
      
    明白了以上区别,就很容易选择了:
    1.exit也是用于不可预期的错误,需要返回它的stack trace信息用来记录,所以不推荐在gen_server中主动调用exit;
    2.throw可以快速回应本次消息的处理,简化代码的嵌套case逻辑,不过要注意,throw出来的一定是一个符合gen_server标准的东西,不然会报错。
    3.正常可预期的逻辑都都使用{stop,Reason, NewState}处理。 
    我们再深入一点:
    当我们stop时的Reason不是 normal | shutdown | {shutdown,term()} 时会在kernel log中打印日志
     
    Notice that for any other reason than normal, shutdown, or {shutdown,Term}, the gen_server process is assumed to terminate because of an error and an error report is issued using error_logger:format/2
    比如这样:
    =ERROR REPORT==== 31-Oct-2016::15:10:44 ===
    ** Generic server test_throw_exit terminating
    ** Last message in was go
    ** When Server state == {state}
    ** Reason for termination ==
    ** not_shutdown_term

    所以,如果我们在正常退出或shutdown时不需要那一列烦人的log,就把reason定义为三者中的一种就行了。

    具体源码可见: https://github.com/erlang/otp/blob/maint/lib/stdlib/src/gen_server.erl#L809

    总结:
    1. 在gen_server中正常退出,请回复{stop, Reason, NewState},
    2. 如果是要快速结束此次消息的退出可以使用throw(term()),其中term()符合gen_server规范(和正常返回的值一样),
    3. 正常逻辑里面不推荐使用exit/error来处理,分携带多余的stack trace信息。 
     

    6. 参考资料:

      Erlang官方文档:http://erlang.org/doc/reference_manual/errors.html

      Learnyousomeerlang关于Exceptions的介绍: http://learnyousomeerlang.com/errors-and-exceptions

  • 相关阅读:
    Leetcode 811. Subdomain Visit Count
    Leetcode 70. Climbing Stairs
    Leetcode 509. Fibonacci Number
    Leetcode 771. Jewels and Stones
    Leetcode 217. Contains Duplicate
    MYSQL安装第三步报错
    .net 开发WEB程序
    JDK版本问题
    打开ECLIPSE 报failed to load the jni shared library
    ANSI_NULLS SQL语句
  • 原文地址:https://www.cnblogs.com/zhongwencool/p/error_exit_throw.html
Copyright © 2011-2022 走看看