Erlang学习笔记(六)顺序程序的错误处理

Erlang最初被设计用来编写容错式系统,这种系统原则上应该永不停歇。
常见的Erlang应用程序是由几十万到几百万个并发程序组成的,拥有大量进程改变了我们对错误处理的看法,在并发性的大程序中,单个进程的崩溃就没有那么重要了,前提是其他某些进程能够察觉到这个崩溃,并接手崩溃进程原本应该做的事情。
要完全了解错误处理,必须先了解顺序程序里的错误处理,理解之后再来看如何处理大量并行进程里的错误。

1. 处理顺序代码里的错误

防御式编程:永远不能让函数对非法的参数返回值,而是应该抛出一个异常错误。
Erlang中常见的内置函数来显示生成一个错误。
exit(Why)
当你确实想要终止当前进程时就用它。如果这个异常错误没有被捕捉到,信号{‘EXIT’, Pid, Why}就会被广播给当前进程链接的所有进程。

throw(Why)
这个函数的作用是抛出一个调用者可能想要捕捉的异常错误。在这种情况下,我们注明了被调用函数可能会抛出这个异常错误。有两种方法可以代替他使用:可以为通常的情形编写代码并且有意疏忽异常错误,也可以把调用封装在一个try…catch表达式里,然后对错误进行处理。

error(Why)
这个函数的作用是指示“崩溃性错误”, 也就是调用者没有准备好处理的非常严重的问题。
它与系统内部生产的错误差不多。

Erlang有两种方法,来捕捉异常错误。第一种是把抛出异常错误的调用封装在一个try…catch表达式里,另一种是把调用封装在一个catch表达式里。

2. 用try…catch捕捉异常错误

Erlang捕捉异常例子:

try FuncOrExpressionSeq of
    Pattern1 [when Guard1] -> Expressions1;
    Pattern2 [when Guard1] -> Expressions2;
    ...
catch
    ExceptionType1:ExPattern1 [when ExGuard1] -> ExExpressions1;
    ExceptionType2:ExPattern2 [when ExGuard2] -> ExExpressions2;
    ...
after
    AfterExpressions
end

注意:Erlang里的一切都是表达式,而表达式都具有值。因此,我们可以这样编写代码:

f(...) ->
...
X = try...end,
Y = g(X),
...

更多情况下,并不需要try…catch表达式的值。所以只需要这样写:

f(...) ->
...
try...end,
...
...

请注意try…catch表达式和case表达式之间具有相似性,try…catch就像是case表达式的强化版。他基本上是case表达式加上了最后的catch和after区块。
try…catch编程样例:

-module(test_try).
-export([generate_exception/1, demo1/0, catcher/1]).

generate_exception(1) -> a;
generate_exception(2) -> throw(a);
generate_exception(3) -> exit(a);
generate_exception(4) -> {'EXIT', a};
generate_exception(5) -> error(a).


demo1() ->
    [catcher(I) || I <- [1,2,3,4,5]].

catcher(N) ->

        try generate_exception(N) of
            Val -> {N, normal, Val}
        catch
            throw:X -> {user, N, caught, thrown, X};
            exit:X -> {user, N, caught, thrown, X};
            error:X ->  {user, N, caught, thrown, X}                
        end.

3. 用catch捕捉异常错误

异常错误如果发生在catch语句中,就会被转换成一个描述此错误的{‘EXIT’, …}元组。
catch错误会提供详细的栈跟踪信息。

4. 练习

(1).file:read_file(File) 会返回 {ok, Bin} 或者 {error, Why},其中 File 是文件名, Bin 则包含了文件的内容。请编写一个myfile:read(File) 函数,当文件可读取时返回 Bin,否则抛出一个异常。

-module(test_file_read).
-export([file_read/1]).
file_read(File) ->
        try file:read_file(File) of
            {ok, Content} -> Content;
            {error, Reason} -> throw(Reason)
        catch
            throw:X -> io:format("throw Reason is: ~p ~n", [X]);
            error:X -> io:format("throw Reason is: ~p ~n", [X]);
            exit:X -> io:format("throw Reason is ~p, ~n", [X])
        end.

(2)重写 test_try.erl 里的代码,让它生成两条错误消息;一条明文消息给用户,另一条详细的消息给开发者。

-module(test_try).
-export([generate_exception/1, demo1/0, catcher/1]).

generate_exception(1) -> a;
generate_exception(2) -> throw(a);
generate_exception(3) -> exit(a);
generate_exception(4) -> {'EXIT', a};
generate_exception(5) -> error(a).


demo1() ->
    [catcher(I) || I <- [1,2,3,4,5]].

catcher(N) ->

        try generate_exception(N) of
            Val -> {N, normal, Val}
        catch
            throw:X -> {user, N, caught, thrown, X},
                       {developer, N, erlang:get_stacktrace()};
            exit:X -> {user, N, caught, thrown, X},
                      {developer, N, erlang:get_stacktrace()};
            error:X ->  {user, N, caught, thrown, X},
                        {developer, N, erlang:get_stacktrace()}

        end.

5. 小结

异常处理在Erlang中是非常重要的,只有先理解并熟悉了简答的顺序程序的错误处理过程,才能帮助以后学习并发性程序的错误处理。
为错误编写代码时要考虑两个关键的原则:
1. 应该在错误发生时立即将它抛出,而且要抛出的明显;
2. 要文明抛出。文明抛出的意思是指,只有程序员才能看到该程序崩溃时产生的详细错误信息,用户绝对不可以看到这个信息。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值