2012-11-29 53 views
6

今天爲了捕捉錯誤問題,我已經閱讀了erlang's錯誤和錯誤處理文檔。何時使用拋/ 1與退出/ 1與Erlang中的錯誤/ 1?

對於生成的錯誤類型,有兩種類型,一種是exit,另一種是throw。 在對我的源代碼進行清理之後,throw and exit表達式已經集合起來。

兩者都是相似的,似乎只是搭配搭配的表達方式不同而已。

([email protected])30> catch throw ({aaa}). 
{aaa} 
([email protected])31> catch exit ({aaa}). 
{'EXIT',{aaa}} 
([email protected])32> catch gen_server:call(aaa,{aaa}). 
{'EXIT',{noproc,{gen_server,call,[aaa,{aaa}]}}} 

你能告訴我什麼時候扔使用,何時使用退出?

回答

12

有類可以與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/1catch也返回{'EXIT',Reason}Reason只包含實際的退出原因。 try ... catch看起來像它等同他們,但他們是/非常不同。

+0

非常感謝您的幫助。現在更清楚了。 –

+0

@ChenYu - 爲我的草率解釋道歉。 rvirding - 謝謝你清理那個。讓我修復/刪除我的答案 - 更好的混淆! – Faiz

6

[增訂]

我掩蓋了錯誤之間的重要區別,由羅伯特·Virding指出。此編輯僅用於記錄!

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,而子進程剛剛死亡。這是Erlang讓它崩潰的哲學。

所以exit/1的EXIT不會在同一個過程中被抓到,而是留給父母。 error/1的錯誤對於該過程是本地的 - 即關於過程本身發生了什麼以及怎樣處理; throw/1用於控制堆棧中的流量。

[更新]

  1. 本教程介紹得好:http://learnyousomeerlang.com/errors-and-exceptions
  2. 請注意,還有一個exit/2 - 調用過程的Pid送出口處。 exit/1意味着父進程。
+0

非常感謝您的信息。 –

+0

@ChenYu如果你閱讀本章,你會發現這裏的解釋是錯誤的。你使用'throw/1'做非本地返回,** NOT **因爲拋出一個錯誤,你使用'error/1'來指示錯誤,並且'exit/1'表示這個過程是死。有** 3 **類的類:'throw','error'和'exit' – rvirding

+0

@rvirding希望我已經對此進行了修改。反饋讚賞! – Faiz

0

我是新來的Erlang,但在這裏就是我想這些東西是什麼,他們之間的分歧,他們已經習慣什麼,等:

throw:應該在本地處理的條件(即在當前的過程中)。例如。調用者正在查找集合中的元素,但不知道集合是否實際上包含這樣的元素;然後,如果這樣的元素不存在,則被叫者可能會拋出,並且呼叫者通過使用try[/of]/catch來檢測缺席。如果主叫方忽略這樣做,那麼這會變成一個nocatcherror(解釋如下)。

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中,紅寶石等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來救援。將normal傳遞給exit會導致進程終止,就像上次函數調用已返回一樣。

因此,通常不宜在通用庫中調用exit,如lists。這個過程是否應該結束,這不是圖書館的業務;這應該由應用程序代碼決定。


異常怎麼樣exit

這一點很重要,如果另一個進程(「遠程」的過程)鏈接到「本地」過程調用exit(和process_flag(trap_exit, true)不叫):就像過去的函數返回時,exit(normal)不會導致遠程進程退出。但是如果本地進程撥打exit(herp_derp),那麼遠程進程也會以Reason=herp_derp退出。當然,如果遠程進程鏈接到更多進程,他們也會通過Reason=herp_derp獲得退出信號。因此,不存在導致連鎖反應。

讓我們來看看這個動作:

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之前和spawn_link後返回相同PID)的第一個過程。但是第二個進程確實導致了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)被調用。精彩!

+0

btw,退出(self(),wat)不等於退出(wat)。當process_flag(trap_exit,true)被使用時(當前線程),兩者的行爲不同:前者導致'EXIT'消息;而後者確實在那裏退出。 – allyourcode

+0

另外,如果你退出(NotSelf,原因),NotSelf使用trap_exit,那麼它會得到一條消息而不是退出,就像遠程(從NotSelf的角度來看)已經退出一樣。對我來說,這真的很不直觀;我認爲trap_exit只與遠程進程退出有關,但它也適用於使用exit/2退出時的self,即使在self()調用時也是如此!我的大腦(和我的感受)現在很痛... – allyourcode

+0

另一件事,當它們由exit/2生成時,我沒有意識到{'EXIT',Pid,Reason}消息:Pid是調用exit/2而不是由exit/2的第一個參數命名的進程。瘋狂... – allyourcode

相關問題