golang panic 和 recover 什麼時候用?

golang panic 和 recover 什麼時候用?

在通常情況下,函數向其調用方報告錯誤的方式都是返回一個error類型的值。但是,

當遇到致命錯誤的時候,很可能會使程序無法繼續運行。這時,上述錯誤處理方式就太

不適合了,Go推薦通過調用panic函數來報告致命錯誤。

1.panic

為了報告運行期間的致命錯誤,Go內建了專用函數panic,該函數用於停止當前的

控制流程並引發一個運行時恐慌。它可以接受一個任意類型的參數值,不過這個參數值

的類型常常會是string或者error,因為這樣更容易描述運行時恐慌的詳細信息。請看下

面的例子:

func main(){
outerFunc()
}

func outerFunc(){
innerFunc()
}

func innerFunc(){
panic(errors.New("An intended fatal errorl"))
}

當調用innerFunc函數中的panic函數後,innerFunc的執行會被停止。緊接著,流程

控制權會交回給調用方 outerFunc函數。然後,outerFunc函數的執行也將被停止。運行時

恐慌就這樣沿著調用棧反方向進行傳播,直至到達當前goroutine的調用棧的最頂層。一

旦達到頂層,就意味著該goroutine調用棧中所有函數的執行都已經被停止了,程序已經

崩潰。

golang panic 和 recover 什麼時候用?

當然,運行時恐慌並不都是通過調用panic函數的方式引發的,也可以由Go的運行

時系統來引發。例如:

myIndex:=4
ia:=[3]int{1,2,3}
_=ia[myIndex]

這個示例中的第3行代碼會引發一個運行時恐慌,因為它造成了一個數組訪問越界

的運行時錯誤。這個運行時恐慌就是由Go的運行時系統報告的。它相當於我們顯式地調

用panic函數並傳入一個runtime.Error類型的參數值。順便說一句,runtime.Error是一個

接口類型,並且內嵌了Go內置的error接口類型。

顯然,我們都不希望程序崩潰。那麼,怎樣“攔截”一個運行時恐慌呢?

2.recover

運行時恐慌一旦被引發,就會向調用方傳播直至程序崩潰。Go提供了專用於“攔截”

運行時恐慌的內建函數recover,它可以使當前的程序從恐慌狀態中恢復並重新獲得流程

控制權。recover 函數被調用後,會返回一個interface{}類型的結果。如果當時的程序正

處於運行時恐慌的狀態,那麼這個結果就會是非nil的。

recover 函數應該與defer語句配合起來使用,例如:

defer func(){
ifp:=recover();pl=nil{
fmt.Printf(“Recovered panic:%s\\n",p)
}
}()

把此類代碼放在函數體的開始處,這樣可以有效防止該函數及其下層調用中的代碼

引發運行時恐慌。一旦發現recover函數的調用結果非ni1,就應該採取相應的措施。

值得一提的是,Go標準庫中有一種常見的用法值得我們參考。請看標準庫代碼包fmt

func(s *ss)Token(skipSpace bool,f func(rune)bool)(tok []byte,err error){

defer func(){

ife:=recover();el=nil{

if se,ok:=e.(scanError);ok{

err=se.err

}else{

panic(e)

}

}

}()

//省略部分代碼

}

golang panic 和 recover 什麼時候用?

在Token函數包含的延遲函數中,當運行時恐慌攜帶值的類型是fmt.scanError時,

這個值就會被賦值給代表結果值的變量err,否則運行時恐慌就會被重新引發。如果這個

重新引發的運行時恐慌傳遞到了調用棧的最頂層,那麼標準輸出上就會打印出類似這樣

的內容:

panic:

panic:

goroutine 1[running]:

main.func·001()

goroutine 2[runnable]:

exit status 2

這裡展現的慣用法有兩個,如下。

可以把運行時恐慌的攜帶值轉換為error類型值,並當作常規結果返回給調用方。

這樣既阻止了恐慌的擴散,又傳遞了引起恐慌的原因。

檢查運行時恐慌攜帶值的類型,並根據類型做不同的後續動作,這樣可以精確地

控制程序的錯誤處理行為。


分享到:


相關文章: