最近在做 DBMXS(Extra Services provided by Data Middleware,数据库中间件) 项目的时候,有个场景需要捕捉进程退出的异常消息。突然很好奇捕捉异常的两个方式 try ... catch 和 case catch ... of 有什么区别。
于是在 google 上搜索一番之后,找到了答案。首先摘抄 Richard Carlsson 在邮件组中的一段话:
Finally, this is also another reason to rewrite old occurrences of 'catch Expr' into 'try Expr catch ... end', because it basically works like this: try Expr catch throw:Term -> Term; exit:Term -> {'EXIT', Term}; error:Term -> {'EXIT', {Term, erlang:get_stacktrace()}} endso what happens if you use one of the following old idioms?: ... catch foo(...), % for side effect, ignore the result ...or case catch foo(...) of {'EXIT', Reason} -> ...; Result -> ... endWell, when the exception type is 'error', the catch will build a result containing the symbolic stack trace, and this will then in the first case be immediately discarded, or in the second case matched on and then possibly discarded later. Whereas if you use try/catch, you can ensure that no stack trace is constructed at all to begin with.简单说就是如果抛出的异常类型是 error,在用 catch 这个关键字直接捕捉的时候,是默认要生成 stack trace 信息的。这个是对性能有一定损耗的。因此对性能敏感的函数逻辑里,建议使用 try ... catch 来代替直接 catch 的方式来捕捉异常。
以下做个实验。首先写一个测试 module:
-module(test).-export([loop/4, try_catch_test/1, try_catch_test/2, catch_test/1, catch_test/2]). loop(0, _, _, _) -> ok;loop(Times, M, F, A) -> _ = apply(M, F, A), loop(Times - 1, M, F, A).try_catch_test(Type) -> try_catch_test(Type, false).try_catch_test(Type, TestLoop) -> try make_an_exception(Type) catch Type:Reason -> case TestLoop of true -> ok; false -> io:format("try .. catch block caught exception of ~p: ~p~n", [Type, Reason]) end end.catch_test(Type) -> catch_test(Type, false).catch_test(Type, TestLoop) -> case catch make_an_exception(Type) of nothing_wrong -> nothing_wrong; Exception -> case TestLoop of true -> ok; false -> io:format("catch block caught exception of ~p~n", [Exception]) end end.make_an_exception(Type) -> case Type of throw -> erlang:throw(ladies); error -> erlang:error(ladies); exit -> erlang:exit(ladies); _ -> nothing_wrong end.然后,分别进行两个实验过程:查看两种异常信息捕捉的实际效果差异和性能差异。
- 实际效果差异
1> c(test).{ok,test}2> test:try_catch_test(throw).try .. catch block caught exception of throw: ladiesok3> test:try_catch_test(error).try .. catch block caught exception of error: ladiesok4> test:try_catch_test(exit). try .. catch block caught exception of exit: ladiesok5> 5> 5> 5> 5> 5> test:catch_test(throw).catch block caught exception of ladiesok6> test:catch_test(error).catch block caught exception of {'EXIT', {ladies, [{test,make_an_exception,1, [{file,"test.erl"},{line,23}]}, {test,catch_test,1, [{file,"test.erl"},{line,14}]}, {erl_eval,do_apply,6, [{file,"erl_eval.erl"},{line,674}]}, {shell,exprs,7, [{file,"shell.erl"},{line,687}]}, {shell,eval_exprs,7, [{file,"shell.erl"},{line,642}]}, {shell,eval_loop,3, [{file,"shell.erl"},{line,627}]}]}}ok7> test:catch_test(exit). catch block caught exception of {'EXIT',ladies}ok8>在使用 try ... catch 去捕捉 throw、error 和 exit 三种异常的时候,都只是捕捉到异常 tag 和异常消息,没有 stack trace。而在使用 catch 直接捕捉异常的时候,捕捉 throw 和 exit 都没有 stack trace,但是在捕捉 error 异常的时候,就同时生成了 stack trace。
- 性能差异
9> timer:tc(test, loop, [10000000, test, try_catch_test, [throw, true]]).{1661010,ok}10> timer:tc(test, loop, [10000000, test, try_catch_test, [error, true]]).{1612950,ok}11> timer:tc(test, loop, [10000000, test, try_catch_test, [exit, true]]). {1628293,ok}12> timer:tc(test, loop, [10000000, test, catch_test, [throw, true]]). {1592246,ok}13> timer:tc(test, loop, [10000000, test, catch_test, [error, true]]).{9939126,ok}14> timer:tc(test, loop, [10000000, test, catch_test, [exit, true]]). {1685309,ok}15>将以上六种场景分别进行 10,000,000 次调用,然后用 timer:tc/3 进行耗时统计。发现在直接用 catch 去捕捉 error 异常的时候,耗时将近 10 秒。而用 try ... catch 方式捕捉 error 异常才花了 1.6 秒左右。可见直接用 catch 去捕捉 error 异常的时候隐式生成 stack trace 对性能的损耗之大。
总结:尽量用 try ... catch 捕捉异常,如果真的需要获取 stack trace,可以在捕捉异常后手动执行 erlang:get_stacktrace/0 来获取。
继续阅读与本文标签相同的文章
上一篇 :
网页特殊符号HTML代码大全
下一篇 :
Java内存模型
-
Linux常见实用命令大全
2026-06-02栏目: 教程
-
网站建设中购买虚拟主机重要参数有哪些?
2026-06-02栏目: 教程
-
如何通过idea连接mysql实现简单的CRUD
2026-06-02栏目: 教程
-
单租户云与多租户云,哪一个适合你?
2026-06-02栏目: 教程
-
云计算为全阿里的增速贡献了多少?
2026-06-02栏目: 教程
