11

你能告诉我什么时候使用throw,exiterror?

1> catch throw ({aaa}).
{aaa}
2> catch exit ({aaa}).
{'EXIT',{aaa}}
3> catch gen_server:call(aaa,{aaa}).
{'EXIT',{noproc,{gen_server,call,[aaa,{aaa}]}}}
4> catch exit("jaj")
{'EXIT',"jaj"}
4

3 回答 3

19

3 个类可以用try ... catch:throw和.errorexit

  • throw使用生成throw/1并且旨在用于非本地返回并且不会生成错误,除非它没有被捕获(当您收到nocatch错误时)。

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

  • exit是使用生成exit/1的,旨在表明该进程将终止。

和之间的区别error/1exit/1没有那么大,更多的是关于错误生成的堆栈跟踪增强的意图。

它们之间的区别实际上在执行时更明显catch ...:当throw/1使用时,则catch返回抛出的值,正如非本地返回所期望的那样;当error/1使用 an 时,catch返回包含堆栈跟踪的{'EXIT',Reason}位置;Reasonwhile fromexit/1 catch也返回{'EXIT',Reason},但Reason仅包含实际退出原因。try ... catch看起来它等同于它们,但它们/曾经非常不同。

于 2012-11-30T22:18:18.177 回答
7

[更新]

Robert Virding 指出,我忽略了throwerror之间的重要区别。此编辑仅用于记录!

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

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

作为一个人为的例子,如果我们想exists为列表实现一个函数,(类似于什么list:any)并且作为一个练习而不自己进行递归,并且使用 just 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.

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

EXIT 将在进程“放弃”时发出信号。进程处理 EXIT,而子进程只是死掉。这就是 Erlang 让它崩溃的哲学。

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

[更新]

  1. 本教程解释得很好: http: //learnyousomeerlang.com/errors-and-exceptions
  2. 请注意,还有一个exit/2- 调用了一个Pid将 EXIT 发送到的进程。 exit/1表示父进程。
于 2012-11-29T03:46:41.390 回答
0

我是 Erlang 的新手,但以下是我对这些东西是什么、它们的区别、它们的用途等的看法:

throw:应该在本地处理的条件(即在当前进程中)。例如,调用者正在寻找集合中的元素,但不知道集合中是否真的包含这样的元素;然后,如果这样的元素不存在,被调用者可以抛出,并且调用者使用try[/of]/catch. 如果调用者忽略了这样做,那么这将变成一个nocatch error(解释如下)。

exit: 当前进程完成。例如,它刚刚完成(在这种情况下,您将传递normal,它被视为与返回的原始函数相同),或者它的操作被取消(例如,它通常无限循环但刚刚收到一条shut_down消息)。

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

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

throw适用于调用者应该处理的正常情况。即处理发生当前进程中。

exit也是理智的,但应该仅仅因为过程完成就结束当前过程。

error疯了。发生了无法合理恢复的事情(通常是错误?),并且本地恢复不合适。


与其他语言相比:

throw类似于 Java 中使用检查异常的方式。然而,error以更类似于未经检查的异常的方式使用。已检查异常是您希望调用者处理的异常。Java 要求您将调用包装起来try/catch或声明您的方法throws出现此类异常。然而,未经检查的异常通常会传播到最外层的调用者。

exit在 Java、C++、Python、JavaScript、Ruby 等更“传统”的语言中没有一个很好的类比。exit模糊地像一个 uber- 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救援的地方。传递normalexit会导致进程终止,就像最后一个函数调用已经返回一样。

因此,exit在通用库中调用通常是不合适的,例如lists. 该过程是否应该结束与图书馆无关。这应该由应用程序代码决定。


异常exit怎么办?

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

让我们看一下实际情况:

1> 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>

我们生成的第一个进程并没有导致 shell 退出(我们可以看出,因为self在 之前和之后返回了相同的 pid spawn_link)。但是第二个进程确实导致 shell 退出(并且系统用新进程替换了 shell 进程)。

当然,如果远程进程使用,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)被视为原始函数返回:

13> spawn_link(fun() -> ok end).
<0.51.0>
14> flush().
Shell got {'EXIT',<0.51.0>,normal}
ok
15> self().
<0.39.0>

你知道什么:和exit(normal)被叫时发生了同样的事情。精彩的!

于 2015-02-08T21:11:44.090 回答