Go 異常處理機制——defer, error, panic, recover

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

一、前言

       在實際的項目中,對於異常的最佳實務很多,在使用不同的語言開發不同類型的程式時,有不同的建議。Google C++ Style 中提到 Google 內部的 C++ 代碼中不使用異常,社區也有很多關於異常的討論。

        作為一門相對來說很新的語言,Go語言中沒有使用傳統的 try...catch 類似的異常處理機制,而是提供了 panic recover 函數來處理所謂的運行時異常,也就是 Google 所稱的錯誤處理機制。配合 defer 語句和 error 介面開發人員可以非常靈活地處理運行時的錯誤和異常。Google 肯定不希望開發人員在代碼中隨便使用 panic,我們知道在風險控制中,有所謂 已知的未知 和 未知的未知,在 Go 程式設計中,對於前者,我們可以通過預先開發的代碼分支來處理,對於後者就比較棘手了:

        1、如果項目中的代碼、使用的標準庫以及第三方庫在運行時內部捕獲了異常並通過合適的 error 對象返回給調用者,那我們可以盡量少甚至可以不用 panic 函數。
        2、如果無法保證上面的情況,那為了確保程式在運行時不會因為 未知的未知 導致崩潰,那 panic 函數的使用可能不得不加在任何需要的地方。

        我們應該認識到,panic是我們和電腦都不希望看到的,應該在設計開發的時候充分考慮使用情境可能出現的情況,處理好 已知的未知,在確定需要的地方使用 panic 機制。

 

二、defer

defer關鍵字用來標記最後執行的Go語句,一般用在資源釋放、關閉串連等操作,會在函數關閉前調用。

多個defer的定義與執行類似於棧的操作:先進後出,最先定義的最後執行。

請先看下邊幾段代碼,然後判斷一下各自輸出內容:

// 範例程式碼一:func funcA() int {    x := 5    defer func() {        x += 1    }()    return x}// 範例程式碼二:func funcB() (x int) {    defer func() {        x += 1    }()    return 5}// 範例程式碼三:func funcC() (y int) {    x := 5    defer func() {        x += 1    }()    return x} // 範例程式碼四:func funcD() (x int) {    defer func(x int) {        x += 1    }(x)    return 5}

解析這幾段代碼,主要需要理解清楚以下幾點知識:

1、return語句的處理過程

        return xxx 語句並不是一條原子指令,其在執行的時候會進行語句分解成 返回變數=xxx return,最後執行return

2、defer語句執行時機

        上文說過,defer語句是在函數關閉的時候調用,確切的說是在執行return語句的時候調用,注意,是return 不是return xxx

3、函數參數的傳遞方式

        Go語言中普通的函數參數的傳遞方式是值傳遞,即新辟記憶體拷貝變數值,不包括slice和map,這兩種類型是引用傳遞

4、變數賦值的傳遞方式

        Go語言變數的賦值跟函數參數類似,也是值拷貝,不包括slice和map,這兩種類型是記憶體引用

按照以上原則,解析代碼:

// 解析代碼一:返回temp的值,在將x賦值給temp後,temp未發生改變,最終傳回值為5func funcA() int {    x := 5    temp=x      #temp變數表示未顯示聲明的return變數    func() {        x += 1    }()    return}// 解析代碼二:返回x的值,先對其複製5,接著函數中改變為6,最終傳回值為6func funcB() (x int) {    x = 5    func() {        x += 1    }()    return}// 解析代碼三:返回y的值,在將x賦值給y後,y未發生改變,最終傳回值為5func funcC() (y int) {    x := 5    y = x       #這裡是值拷貝    func() {        x += 1    }()    return}// 解析代碼四:返回x的值,傳遞x到匿名函數中執行時,傳遞的是x的拷貝,不影響外部x的值,最終傳回值為5func funcD() (x int) {    x := 5    func(x int) { #這裡是值拷貝        x += 1    }(x)    return}

 

三、Error

       Go語言 通過支援多傳回值,讓在運行時返回詳細的錯誤資訊給調用者變得非常方便。我們可以在編碼中通過實現 error 介面類型來建置錯誤資訊,error 介面的定義如下:

type error interface {    Error() string}

還是通過下面的例子來看看:

package mainimport ("fmt")// 定義一個 DivideError 結構type DivideError struct {dividee intdivider int}// 實現 `error` 介面func (de *DivideError) Error() string {strFormat := `Cannot proceed, the divider is zero.dividee: %ddivider: 0`return fmt.Sprintf(strFormat, de.dividee)}// 定義 `int` 類型除法運算的函數func Divide(varDividee int, varDivider int) (result int, errorMsg string) {if varDivider == 0 {dData := DivideError{dividee: varDividee,divider: varDivider,}errorMsg = dData.Error()return} else {return varDividee / varDivider, ""}}func main() {// 正常情況if result, errorMsg := Divide(100, 10); errorMsg == "" {fmt.Println("100/10 = ", result)}// 當被除數為零的時候會返回錯誤資訊if _, errorMsg := Divide(100, 0); errorMsg != "" {fmt.Println("errorMsg is: ", errorMsg)}}

運行後可以看到下面的輸出:

100/10 =  10errorMsg is:  Cannot proceed, the divider is zero.dividee: 100divider: 0

 

四、Panic recover

定義如下:

func panic(interface{})func recover() interface{}

panicrecover 是兩個內建函數,用於處理 run-time panics 以及程式中自訂的錯誤。

        當執行一個函數 F 的時候,如果顯式地調用 panic 函數或者一個 run-time panics 發生時,F 會結束運行,所有 F 中 defer 的函數會按照 FILO 的規則被執行。之後,F 函數的調用者中 defer 的函數再被執行,如此一直到最外層代碼。這時,程式已經被中斷了而且錯誤也被一層層拋出來了,其中包括 panic 函數的參數。當前被中斷的 goroutine 被稱為處於 panicking 狀態。由於 panic 函數的參數是空介面類型,所以可以接受任何類型的對象:

panic(42)panic(42)panic("unreachable")panic(Error("cannot parse"))

       recover 函數用來擷取 panic 函數的參數資訊,只能在延時調用 defer 語句調用的函數中直接調用才會生效,如果在 defer 語句中也調用 panic 函數,則只有最後一個被調用的 panic 函數的參數會被 recover 函數擷取到。如果 goroutine 沒有 panic,那調用 recover 函數會返回 nil

package mainimport ("fmt")// 最簡單的例子func SimplePanicRecover() {defer func() {if err := recover(); err != nil {fmt.Println("Panic info is: ", err)}}()panic("SimplePanicRecover function panic-ed!")}// 當 defer 中也調用了 panic 函數時,最後被調用的 panic 函數的參數會被後面的 recover 函數擷取到// 一個函數中可以定義多個 defer 函數,按照 FILO 的規則執行func MultiPanicRecover() {defer func() {if err := recover(); err != nil {fmt.Println("Panic info is: ", err)}}()defer func() {panic("MultiPanicRecover defer inner panic")}()defer func() {if err := recover(); err != nil {fmt.Println("Panic info is: ", err)}}()panic("MultiPanicRecover function panic-ed!")}// recover 函數只有在 defer 函數中被直接調用的時候才可以擷取 panic 的參數func RecoverPlaceTest() {// 下面一行代碼中 recover 函數會返回 nil,但也不影響程式運行defer recover()// recover 函數返回 nildefer fmt.Println("recover() is: ", recover())defer func() {func() {// 由於不是在 defer 調用函數中直接調用 recover 函數,recover 函數會返回 nilif err := recover(); err != nil {fmt.Println("Panic info is: ", err)}}()}()defer func() {if err := recover(); err != nil {fmt.Println("Panic info is: ", err)}}()panic("RecoverPlaceTest function panic-ed!")}// 如果函數沒有 panic,調用 recover 函數不會擷取到任何資訊,也不會影響當前進程。func NoPanicButHasRecover() {if err := recover(); err != nil {fmt.Println("NoPanicButHasRecover Panic info is: ", err)} else {fmt.Println("NoPanicButHasRecover Panic info is: ", err)}}// 定義一個調用 recover 函數的函數func CallRecover() {if err := recover(); err != nil {fmt.Println("Panic info is: ", err)}}// 定義個函數,在其中 defer 另一個調用了 recover 函數的函數func RecoverInOutterFunc() {defer CallRecover()panic("RecoverInOutterFunc function panic-ed!")}func main() {SimplePanicRecover()MultiPanicRecover()RecoverPlaceTest()NoPanicButHasRecover()RecoverInOutterFunc()}

運行後可以看到下面的輸出:

Panic info is:  SimplePanicRecover function panic-ed!Panic info is:  MultiPanicRecover function panic-ed!Panic info is:  MultiPanicRecover defer inner panicPanic info is:  RecoverPlaceTest function panic-ed!recover() is:  <nil>NoPanicButHasRecover Panic info is:  <nil>Panic info is:  RecoverInOutterFunc function panic-ed!

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.