什么时候在Erlang中使用throw / 1 vs. exit / 1 vs. error / 1?

时间:2012-11-29 03:17:53

标签: erlang

今天发现错误问题,我已阅读erlang's错误和错误处理文档。

对于生成的错误类型,有两种类型,一种是exit,另一种是throw。 在使用我的源代码后,throw and exit表达式已经集中。

两者都相似,似乎只是捕捉匹配表达式不同。

(emacs@chen-yumatoMacBook-Pro.local)30> catch throw ({aaa}).
{aaa}
(emacs@chen-yumatoMacBook-Pro.local)31> catch exit ({aaa}).
{'EXIT',{aaa}}
(emacs@chen-yumatoMacBook-Pro.local)32> catch gen_server:call(aaa,{aaa}).
{'EXIT',{noproc,{gen_server,call,[aaa,{aaa}]}}}
  

你能告诉我什么时候使用throw以及何时使用exit?

3 个答案:

答案 0 :(得分:15)

3 类可以使用try ... catch throwerrorexit来抓住。

  • throw是使用throw/1生成的,旨在用于非本地返回,除非未捕获,否则不会生成错误(当您收到nocatch错误。

  • 系统检测到错误时会生成
  • error。您可以使用error/1显式生成错误。系统还在生成的错误值中包含堆栈跟踪,例如{badarg,[...]}

  • exit是使用exit/1生成的,用于表示此过程将会死亡。

error/1exit/1之间的差异不是很大,更多的是错误产生的堆栈跟踪增强的意图。

在执行catch ...时,它们之间的差异实际上更明显:当使用throw/1时,catch只返回抛出的值,正如非本地返回所期望的那样;当使用error/1时,catch会返回{'EXIT',Reason},其中Reason包含堆栈跟踪;来自exit/1 catch的同时返回{'EXIT',Reason},但Reason仅包含实际退出原因。 try ... catch看起来等同于他们,但他们是非常不同的。

答案 1 :(得分:6)

[已更新]

我掩盖了罗伯特·维丁指出的抛出错误之间的重要区别。此编辑仅供记录!

throw error用于在其他语言中使用throw的地方。您的代码检测到正在运行的进程中的错误,该错误通过error/1发出异常信号。相同的进程捕获它(可能在堆栈中更高),并且错误将在同一进程中处理。 error总是带来一个堆栈跟踪。

throw用于不发出错误信号,只是用于从深层嵌套函数返回值。 由于它展开堆栈,因此调用throw会将抛出的值返回到捕获的位置。就像error的情况一样,我们正在捕获被抛出的东西,只有被抛出的东西不是错误,而只是一个传递给堆栈的值。这就是为什么throw不会带来堆栈跟踪的原因。

作为一个人为的例子,如果我们想要为列表实现exists函数(类似于list:any所做的那样)并且在没有的情况下执行练习而不进行递归我们自己,只使用list:foreach,然后可以使用throw

exists(P, List) ->
  F = fun(X) -> 
    case P(X) of 
      true -> throw(true); 
      Whatever -> Whatever 
    end
  end,
  try lists:foreach(F, List) of
    ok -> false
  catch
   true -> true
  end.

抛出但未捕获的值被视为error:将生成nocatch异常。

'放弃'时,EXIT将由进程发出信号。 进程处理EXIT,而子进程就死了。这是Erlang let-it-crash理念。

所以exit/1的EXIT不会被捕获在同一个进程中,而是留给父进程。 error/1的错误是流程的本地错误 - 即,流程本身发生了什么以及如何处理; throw/1用于跨堆栈的控制流。

<强> [UPDATE]

  1. 本教程很好地解释了它:http://learnyousomeerlang.com/errors-and-exceptions
  2. 请注意,还有一个exit/2 - 使用 Pid 调用要将EXIT发送到的进程。 exit/1表示父进程。

答案 2 :(得分:0)

我是Erlang的新手,但我想到的是这些东西是什么,它们的区别,它们用于什么等等:

throw:应在本地处理的条件(即在当前流程中)。例如。调用者正在寻找集合中的元素,但不知道集合是否实际包含这样的元素;然后,如果这样的元素不存在,被调用者可以抛出,并且调用者通过使用try[/of]/catch来检测缺席。如果调用者忽略了这一点,那么这会变成nocatch error(如下所述)。

exit:当前流程已完成。例如。它已经简单地完成了(在这种情况下,你传递normal,它被视为与返回的原始函数相同),或者它的操作被取消(例如,它通常无限循环,但刚收到{{ 1}}消息)。

shut_down:进程已经做了某些事情和/或达到程序员没有考虑的状态(例如1/0),认为是不可能的(例如error遇到一个值不匹配任何情况),或不满足一些先决条件(例如输入是非空的)。在这种情况下,本地恢复没有意义。因此,case ... ofthrow都不合适。由于这是意料之外的,因此堆栈跟踪是Reason的一部分。

如您所见,上面的列表按升序排列:

exit用于调用者应该处理的理智条件。即处理当前进程中发生。

throw也是理智的,但应该仅仅因为流程已经完成而结束当前流程。

exit很疯狂。发生了无法合理恢复的事情(通常是一个错误?),本地恢复不合适。


VS。其他语言:

error类似于Java中使用的检查异常的方式。然而,throw的使用方式更类似于未经检查的例外。已检查的异常是您希望调用者处理的异常。 Java要求您在error中包装调用或声明您的方法try/catch这样的例外。然而,未经检查的异常通常会传播到最外面的调用者。

throws在Java,C ++,Python,JavaScript,Ruby等更“常规”的语言中没有很好的类比。exit模糊地像一个超级 - exit:而不是最后返回,你可以从一个函数的中间返回,除了你不是从当前函数返回,你从它们返回ALL。


return示例

exit

由于serve_good_times() -> receive {top_of_the_mornin, Sender} -> Sender ! and_the_rest_of_the_day_to_yourself; {you_suck, Sender} -> Sender ! take_a_chill_pill; % More cases... shut_down -> exit(normal) end, serve_good_times() end 在几乎所有消息之后调用自身,程序员已决定我们不希望在每个接收情况下重复该调用。因此,她在接收之后接听了电话。但是,如果serve_good_times决定停止自我调用怎么办?这是serve_good_times来救援的地方。将exit传递给normal会导致进程终止,就像最后一次函数调用已经返回一样。

因此,在exit等通用库中调用exit通常是不合适的。图书馆的业务是否应该结束;这应该由应用程序代码决定。


异常lists怎么办?

如果另一个进程(“远程”进程)链接到调用exit(并且未调用exit)的“本地”进程,则这很重要:就像返回的最后一个函数一样,{ {1}}不会导致远程进程退出。但是,如果本地进程进行process_flag(trap_exit, true)调用,则远程进程也会以exit(normal)退出。当然,如果远程进程链接到更多进程,它们也会获得exit(herp_derp)的退出信号。因此,非Reason=herp_derp退出会导致连锁反应。

让我们来看看这个:

Reason=herp_derp

我们生成的第一个进程没有导致shell退出(我们可以告诉,因为normal1> self(). <0.32.0> 2> spawn_link(fun() -> exit(normal) end). <0.35.0> 3> self(). <0.32.0> 4> 4> 4> spawn_link(fun() -> exit(abnormal) end). ** exception exit: abnormal 5> self(). <0.39.0> 6> 之前和之后返回了相同的pid)。但是第二个进程确实导致shell退出(并且系统用一个新进程替换了shell进程)。

当然,如果远程进程使用self,那么它只会收到一条消息,无论本地进程是否将spawn_link或其他内容传递给process_flag(trap_exit, true)。设置此标志可以阻止连锁反应。

normal

回想一下,我说exit被视为原始函数返回:

6> process_flag(trap_exit, true).
false
7> spawn_link(fun() -> exit(normal) end).  
<0.43.0>
8> self().
<0.39.0>
9> flush().
Shell got {'EXIT',<0.43.0>,normal}
ok
10>                                         
10> 
10> spawn_link(fun() -> exit(abnormal) end).
<0.47.0>
11> self(). 
<0.39.0>
12> flush().
Shell got {'EXIT',<0.47.0>,abnormal}

你知道什么:同样的事情发生在调用exit(normal)的时候。精彩!