Erlang --- 异常处理

异常可以认为是函数的另一种返回形式,区别在于它不仅会返回至调用者,还会返回至调用者的调用者,并一路向上,直到被捕获或抵达进程调用的起点(这时进程便会崩溃)为止。
Erlang的异常分为三类:
error —— 这类是运行时异常,在发生除零错误、匹配运算失败、找不到匹配的函数子句等情况时触发。这些异常的特点在于一旦它们促使某个进程崩溃,Erlang错误日志管理器便会将之记录在案。
exit —— 这类异常用于通报“进程即将停止”。它们会在迫使进程崩溃的同时将进程退出的原因告知给其他的进程,因此一般不捕获这类异常,exit也在进程正常终止时使用,这时它会令进程退出并通报“任务结束,一切正常”。无论是哪种情况,进程因exit而终止都不算是意外事件,因而也不会被汇报至错误日志管理器。
throw —— 这类异常用于处理用户自定义的情况。你可以用throw来通报你的函数遭遇了某种意外,也可以用它来完成所谓的非局部返回或是用于跳出深层递归。如果进程没能捕获throw异常,它便会转变成一个原因为nocatch的error异常,迫使进程终止并记录日志。
针对每种异常,都有一个与之对应的用于抛出异常的内置函数:throw(SomeTerm) , exit(Reason) , erlang:error(Reason)
在现代Erlang中,你可以用try表达式来处理某段代码中发生的异常,其形式为
try
     some_unsafe_function()
catch
     oops               -> got_throw_oops;
     throw:Other  -> {got_throw,Other};
     exit:Reason    -> {got_exit,Reason};
     error:Reason  -> {got_error,Reason}
end
位于try和catch之间的是正文或保护区,在正文内抛出并试图传播出去的任何异常都会被捕获并与catch和end之间列出的子句做匹配。如果没有匹配的子句,那么该异常会继续传播,就好像没有正文外围的try表达式一样,与此类似,如果正文在求值过程中没有触发任何异常,那么正文的结果也就是整个表达式的结果,就好像try和catch...end根本不存在。唯一的区别在于,一旦异常发生且能够与某个子句相匹配,该子句的结果便会成为整个表达式的结果。
这些子句的模式有些特殊——它们可以用冒号(:)作为异常的类别(error、exit或throw)和被抛出的项式的分隔符。如果省略类别,则默认为throw。一般情况下不应该去捕获error和exit,除非你明确知道自己在做什么。这种做法违背了速错的理念,还有可能掩盖真正的症结。某些情况下,你需要运行一些不那么可信的代码并捕获从中抛出的所有东西。这时你可以使用以下模式来捕获所有异常:
_:_      -> got_some_exception
另外,需要注意的是一旦进入catch部分,代码就不再受到保护。catch子句中抛出的新的异常,会传播到try表达式之外。
当你需要区分正常情况和异常情况并做出不同的处理时,可以使用try的复杂形式。
try
     some_unsafe_function
of
     0 -> io:format("nothing to do ~n");
     N -> do_something_with(N)
catch
     _:_ -> io:format("some problem")
end
求值成功后你要做的第一件事(除了给求值结果命名)一般都是以求值结果为条件做分支判断,你可以像在case表达式中那样在of和catch之间写上一个或多个子句,用于处理try...of部分求值成功后的逻辑。但要注意,of....部分和catch部分一样,是不在保护范围内的——当前的try表达式无法捕获这里发生的异常。
最后,你还可以给任意try表达式加上一个after段。其作用在于确保某段具有副作用的代码的执行。在你离开try表达式之前,无论表达式的其余部分发生了什么,after段的代码都会被执行。这种机制通常用于完成各种形式的资源释放。
注意,只有整个try表达式全部就绪之后after部分的代码才会执行,这里所谓的就绪也包括从of部分或catch的某个子句中又抛出新的异常的情况。若确实抛出了新的异常,该异常会被暂时挂起,直到after部分执行完毕后再被重新抛出。如果after部分又抛出异常,抛出的异常便会取代之前的异常,而原先被挂起的异常则会被丢弃。
获取栈轨迹
通常,你所见到的异常并不包含执行栈的轨迹,它被存储于内部。你可以通过调用内置函数erlang:get_stacktrace()来查看当前进程最近抛出的异常的栈轨迹。
栈轨迹(stack_trace)是异常发生那一刻位于栈的顶部的那些调用的逆序列表,每个函数都被表示成{Module,Function,Args}的形式,其中Module和Function都是原子,Args要么是函数的元数,要么是函数被调用时的参数列表,这取决于当时的可用信息的情况。一般来说,只能看到最顶层调用的参数列表。
注意,如果调用erlang:get_stacktrace()后得到一个空表,就表示直至目前为止该进程尚未捕获任何异常。
posted @ 2016-04-27 16:22  孔夫子耍大刀  阅读(1268)  评论(0编辑  收藏  举报