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 ... catchthrowerrorexit

  • throwを使用して生成され、非ローカルリターンthrow/1に使用することを目的としており、キャッチされない限り(エラーが発生した場合)、エラーは生成されません。nocatch

  • errorシステムがエラーを検出すると生成されます。を使用して明示的にエラーを生成できますerror/1。システムは、生成されたエラー値にスタックトレースも含めます。たとえば、{badarg,[...]}

  • exitを使用して生成さexit/1れ、このプロセスが終了することを通知することを目的としています。

error/1との違いexit/1はそれほど大きくはありません。エラーによって生成されたスタックトレースが強化する意図についてです。

それらの違いは、実際には次のcatch ...場合により顕著になります。throw/1を使用するとcatch、非ローカルリターンから予想されるように、がスローされた値を返すだけです。anerror/1を使用すると、スタックトレースが含まれる場所がcatch返されます。from fromも戻りますが、実際の終了理由のみが含まれます。それはそれらを同一視しているように見えますが、それらは/非常に異なっていました。{'EXIT',Reason}Reasonexit/1 catch{'EXIT',Reason}Reasontry ... catch

于 2012-11-30T22:18:18.177 に答える
7

[更新しました]

私は、Robert Virdingが指摘した、スローエラーの重要な違いについて詳しく説明しました。この編集は記録のためだけです!

throw は、他の言語でerror使用する場所で使用されます。throw実行中のプロセスのエラーがコードによって検出されました。これは、。で例外を通知しますerror/1。同じプロセスがそれをキャッチし(おそらくスタックの上位)、エラーは同じプロセス内で処理されます。error常にスタックトレースをもたらします。

throwエラーを通知するためではなく、深くネストされた関数から値を返すために使用されます。スタックを巻き戻すので、呼び出しthrowはスローされた値をキャッチされた場所に返します。の場合と同様に、errorスローされたものをキャッチしています。スローされたものだけがエラーではなく、スタックに渡された値だけでした。これが、throwがスタックトレースをもたらさない理由です。

exists不自然な例として、リストの関数を実装したい場合(実行するのと同様list:any)、自分で再帰を行わずに演習として、を使用してlist:foreachthrowここで使用できます。

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スタック全体の制御フローに使用されます。

[アップデート]

  1. このチュートリアルはそれをよく説明しています:http://learnyousomeerlang.com/errors-and-exceptions
  2. EXITを送信するプロセス のexit/2-で呼び出される-もあることに注意してください。親プロセスを意味します。Pidexit/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適切ではありません。これは予期しないことなので、スタックトレースはReasonの一部です。

ご覧のとおり、上記のリストはエスカレーション順になっています。

throw発信者が処理することが期待される正常な状態のためのものです。つまり、処理は現在のプロセス内で行われます。

exitも正気ですが、プロセスが完了したという理由だけで現在のプロセスを終了する必要があります。

error非常識です。合理的に回復できない何かが発生し(通常はバグ?)、ローカル回復は適切ではありません。


対他の言語:

throwチェックされた例外がJavaで使用される方法に類似しています。一方、errorは、チェックされていない例外により類似した方法で使用されます。チェックされた例外は、呼び出し元に処理させたい例外です。Javaでは、呼び出しをラップするか、メソッドでそのような例外try/catchを宣言する必要があります。throws一方、チェックされていない例外は通常、最も外側の呼び出し元に伝播します。

exitJava、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救いの手を差し伸べるところです。normaltoを渡すとexit、最後の関数呼び出しが戻ったかのようにプロセスが終了します。

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>

スポーンした最初のプロセスでは、シェルは終了しませんでした(self前後で同じpidが返されたため、わかりますspawn_link)。しかし、2番目のプロセスによってシェルが終了しました(システムはシェルプロセスを新しいものに置き換えました)。

もちろん、リモートプロセスが使用する場合は、ローカルプロセスがに渡されるか、他の何かがにprocess_flag(trap_exit, true)渡されるかに関係なく、メッセージを受け取るだけです。このフラグを設定すると、連鎖反応が停止します。normalexit

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 に答える