C語言中文網 目錄
首頁 > Go語言教程 > Go語言函數 閱讀:3,840

Go語言宕機恢復(recover)——防止程序崩潰

無論是代碼運行錯誤由 Runtime 層拋出的 panic 崩潰,還是主動觸發的 panic 崩潰,都可以配合 defer 和 recover 實現錯誤捕捉和恢復,讓代碼在發生崩潰后允許繼續運行。

提示

在其他語言里,宕機往往以異常的形式存在。底層拋出異常,上層邏輯通過 try/catch 機制捕獲異常,沒有被捕獲的嚴重異常會導致宕機,捕獲的異常可以被忽略,讓代碼繼續運行。

Go 沒有異常系統,其使用 panic 觸發宕機類似于其他語言的拋出異常,那么 recover 的宕機恢復機制就對應 try/catch 機制。

讓程序在崩潰時繼續執行

下面的代碼實現了 ProtectRun() 函數,該函數傳入一個匿名函數或閉包后的執行函數,當傳入函數以任何形式發生 panic 崩潰后,可以將崩潰發生的錯誤打印出來,同時允許后面的代碼繼續運行,不會造成整個進程的崩潰。

保護運行函數:
package main

import (
    "fmt"
    "runtime"
)

// 崩潰時需要傳遞的上下文信息
type panicContext struct {
    function string // 所在函數
}

// 保護方式允許一個函數
func ProtectRun(entry func()) {

    // 延遲處理的函數
    defer func() {

        // 發生宕機時,獲取panic傳遞的上下文并打印
        err := recover()

        switch err.(type) {
        case runtime.Error: // 運行時錯誤
            fmt.Println("runtime error:", err)
        default: // 非運行時錯誤
            fmt.Println("error:", err)
        }

    }()

    entry()

}

func main() {
    fmt.Println("運行前")

    // 允許一段手動觸發的錯誤
    ProtectRun(func() {

        fmt.Println("手動宕機前")

        // 使用panic傳遞上下文
        panic(&panicContext{
            "手動觸發panic",
        })

        fmt.Println("手動宕機后")
    })

    // 故意造成空指針訪問錯誤
    ProtectRun(func() {

        fmt.Println("賦值宕機前")

        var a *int
        *a = 1

        fmt.Println("賦值宕機后")
    })

    fmt.Println("運行后")
}
代碼輸出結果:

運行前
手動宕機前
error: &{手動觸發panic}
賦值宕機前
runtime error: runtime error: invalid memory address or nil pointer
dereference
運行后

對代碼的說明:
  • 第 9 行聲明描述錯誤的結構體,成員保存錯誤的執行函數。
  • 第 17 行使用 defer 將閉包延遲執行,當 panic 觸發崩潰時,ProtectRun() 函數將結束運行,此時 defer 后的閉包將會發生調用。
  • 第 20 行,recover() 獲取到 panic 傳入的參數。
  • 第 22 行,使用 switch 對 err 變量進行類型斷言。
  • 第 23 行,如果錯誤是有 Runtime 層拋出的運行時錯誤,如空指針訪問、除數為 0 等情況,打印運行時錯誤。
  • 第 25 行,其他錯誤,打印傳遞過來的錯誤數據。
  • 第 44 行,使用 panic 手動觸發一個錯誤,并將一個結構體附帶信息傳遞過去,此時,recover 就會獲取到這個結構體信息,并打印出來。
  • 第 57 行,模擬代碼中空指針賦值造成的錯誤,此時會由 Runtime 層拋出錯誤,被 ProtectRun() 函數的 recover() 函數捕獲到。

panic和recover的關系

panic 和 defer 的組合有如下特性:
  • 有 panic 沒 recover,程序宕機。
  • 有 panic 也有 recover 捕獲,程序不會宕機。執行完對應的 defer 后,從宕機點退出當前函數后繼續執行。

提示

雖然 panic/recover 能模擬其他語言的異常機制,但并不建議代表編寫普通函數也經常性使用這種特性。

在 panic 觸發的 defer 函數內,可以繼續調用 panic,進一步將錯誤外拋直到程序整體崩潰。

如果想在捕獲錯誤時設置當前函數的返回值,可以對返回值使用命名返回值方式直接進行設置。

精美而實用的網站,提供C語言C++STLLinuxShellJavaGo語言等教程,以及socketGCCviSwing設計模式JSP等專題。

Copyright ?2011-2018 biancheng.net, 陜ICP備15000209號

底部Logo