1. 程式人生 > >go panic與recover分析及錯誤處理

go panic與recover分析及錯誤處理

先介紹一下什麼叫error 型別
這裡寫圖片描述

error 是一種型別,表示錯誤狀態的型別,如果沒有錯誤則是nil。直白點將:error 型別就是描述錯誤的一種型別。

panic 在golang goroutine 的作用
panic 官方文件介紹:
panic 官方文件介紹

panic 是用來停止當前程式的執行。當一個方法呼叫panic。 當函式F呼叫panic時,F的正常執行立即停止。 但是任何有F推遲的函式都會執行,意思是F定義有defer關鍵字宣告的函式會執行,然後F返回給它的呼叫者。 對於呼叫者G來說,F的呼叫就像呼叫panic 一樣,終止G的執行並執行任何延遲(帶有defer 關鍵字)的函式。 這種情況會持續下去,直到正在執行的goroutine中的所有功能都以相反的順序停止。 此時,程式終止並報告錯誤情況,包括panic的引數值。最後這種情況可以通過呼叫recover 來恢復函式的執行。
函式 recover 介紹
函式func 官方解釋


recover內建函式允許一段程式管理一個正在paincing goroutine的行為。

在defer 定義的函式(不是由它呼叫的任何函式)內部執行一段recover 函式,通過recover函式執行來停止panic 函式的執行,並且可以找出給panic所傳遞的錯誤值。 如果在defer 函式之外呼叫恢復,它不會停止panic的執行。 在這種情況下,或者當goroutine沒有panicing時,或者提供給panicing的引數為零時,恢復返回nil。 因此,recover函式的返回值報告協程是否正在遭遇panicing 。

panic函式就是往外扔錯誤,一層接一層往上扔直到當前程式不能執行為止,不想讓panic 函式扔的錯誤導致程式掛掉,就得使用recover 函式來接收panic 錯誤或者說是阻擋panicing ,並且recover 函式可以將錯誤轉化為error 型別。因為panic 錯誤不會讓defer 關鍵字定義的函式也停止執行,就是說defer 關鍵字宣告的函式或者程式碼即使遇到錯誤也會執行。
一個函式裡面有defer 關鍵字宣告一個函式(假設叫catch 函式)和要執行出錯的程式碼,在catch 函式裡面呼叫recover 函式。recover 會攔截錯誤,不會讓錯誤往上扔,返回給呼叫者error(裡面有錯誤的資訊)型別 ,從而使goroutine 不掛掉。
上程式碼:

package main


import (
    "fmt"
    "errors"
)

func main() {
    testError()
    afterErrorfunc()
}

func testError() {
    //defer catch()
    panic(" \"panic 錯誤\"")
    fmt.Println("丟擲一個錯誤後繼續執行程式碼")
}
func  catch()  {
    if r := recover(); r != nil {
        fmt.Println("testError() 遇到錯誤:", r)
        var
err error switch x := r.(type) { case string: err = errors.New(x) case error: err = x default: err = errors.New("") } if err != nil { fmt.Println("recover後的錯誤:",err) } } } func afterErrorfunc(){ fmt.Println("遇到錯誤之後 func ") }

執行結果:

panic:  "panic 錯誤"

goroutine 1 [running]:
main.testError()
    E:/goCode/src/MyTestGo/src/com.dylan.main/panic/testpanic.go:16 +0x40
main.main()
    E:/goCode/src/MyTestGo/src/com.dylan.main/panic/testpanic.go:10 +0x27

Process finished with exit code 2

當panic 函式執行的時候導致後面函式 afterErrorfunc() 不能執行,main函式也丟擲一個錯誤,整個程式異常退出。
通過defer 關鍵字呼叫 catch函式。

func testError() {
    defer catch()
    panic(" \"panic 錯誤\"")
    fmt.Println("丟擲一個錯誤後繼續執行程式碼")
}

程式執行結果:

testError() 遇到錯誤:  "panic 錯誤"
recover後的錯誤:  "panic 錯誤"
遇到錯誤之後 func 
Process finished with exit code 0

分析:程式正常結束,沒有因為panic(錯誤)而到導致程式終止掛掉。錯誤被recover 函式接收,轉化為error型別的錯誤,最後輸出“ recover後的錯誤: “panic 錯誤” ” 而且後面 afterErrorfunc()執行。

一般在寫的時候這麼寫 不用定義catch 函式:

func main() {
    testError()
    afterErrorfunc()
}

func testError() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("testError() 遇到錯誤:", r)
        }
    }()

    panic(" \"panic 錯誤\"")
    fmt.Println("丟擲一個錯誤後繼續執行程式碼")
}
func catch(err error) {
    if r := recover(); r != nil {
        fmt.Println("testError() 遇到錯誤:", r)

        switch x := r.(type) {
        case string:
            err = errors.New(x)
        case error:
            err = x
        default:
            err = errors.New("")
        }
    }
}

func afterErrorfunc() {
    fmt.Println("遇到錯誤之後 func ")
}

在發生panic 函式裡面加入下述程式碼就可以攔截panicing, 並且不讓程式掛掉和顯示錯誤資訊。

defer func() {
        if r := recover(); r != nil {
            fmt.Println("testError() 遇到錯誤:", r)
        }
    }()

最後如果想將錯誤資訊返回給呼叫者可以這麼做:

func main() {
    err := testError()
    if err != nil {
        fmt.Println("main 函式得到錯誤型別:", err)
    }
    afterErrorfunc()
}

func testError() (err error) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("testError() 遇到錯誤:", r)
            switch x := r.(type) {
            case string:
                err = errors.New(x)
            case error:
                err = x
            default:
                err = errors.New("")
            }
        }
    }()
    panic(" \"panic 錯誤\"")
    fmt.Println("丟擲一個錯誤後繼續執行程式碼")
    return nil
}

func catch(err error) {
    if r := recover(); r != nil {
        fmt.Println("testError() 遇到錯誤:", r)
        switch x := r.(type) {
        case string:
            err = errors.New(x)
        case error:
            err = x
        default:
            err = errors.New("")
        }
    }
}

func afterErrorfunc() {
    fmt.Println("遇到錯誤之後 func ")
}

提前宣告一個error 型別的變數。把錯誤資訊傳遞給error變數,再把error變數返回給呼叫者。