ZetCode

Golang recover 函数

最后修改时间 2025 年 5 月 8 日

本教程将介绍如何在 Go 中使用内置的 recover 函数。我们将通过实际的正确错误处理示例来讲解 panic 恢复基础知识。

recover 函数用于重新获得一个正在 panic 的 goroutine 的控制权。它会停止 panic 序列并返回传递给 panic 的值。Recover 仅在 defer 的函数中有用。

在 Go 中,recoverpanicdefer 配合使用,以处理异常情况。它允许在意外错误后进行优雅的关闭或继续执行。

基本的 panic 恢复示例

recover 最简单的用法是捕获 panic 并阻止程序崩溃。此示例演示了基本的 panic 恢复。
注意: Recover 必须在 defer 的函数中调用。

basic_recover.go
package main

import "fmt"

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
        }
    }()
    
    panic("something went wrong")
    
    fmt.Println("This line won't be executed")
}

当发生 panic 时,defer 的函数会调用 recover。在 defer 的函数完成后,程序将继续执行。

从零除恢复

我们可以使用 recover 来处理运行时错误,例如零除。此示例展示了数学运算中的实际错误恢复。

division_recover.go
package main

import "fmt"

func safeDivide(a, b int) (result int, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("division error: %v", r)
        }
    }()
    
    result = a / b
    return result, nil
}

func main() {
    res, err := safeDivide(10, 0)
    if err != nil {
        fmt.Println("Error:", err)
    } else {
        fmt.Println("Result:", res)
    }
}

safeDivide 函数可以从零除 panic 中恢复。它将 panic 转换为常规的错误返回值。

在 goroutine 中恢复

每个 goroutine 都需要自己的 panic 恢复。此示例展示了如何正确处理并发代码中的 panic。

goroutine_recover.go
package main

import (
    "fmt"
    "time"
)

func worker() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Worker recovered:", r)
        }
    }()
    
    fmt.Println("Worker started")
    time.Sleep(1 * time.Second)
    panic("worker panic")
    fmt.Println("Worker finished")
}

func main() {
    go worker()
    
    time.Sleep(2 * time.Second)
    fmt.Println("Main function completed")
}

工作 goroutine 有自己的恢复机制。由于有适当的恢复,panic 不会影响主 goroutine。

使用日志记录进行恢复

我们可以将 recover 与详细的错误日志记录结合起来。此示例演示了带堆栈跟踪的高级 panic 恢复。

logging_recover.go
package main

import (
    "fmt"
    "runtime/debug"
)

func riskyOperation() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered from panic:", r)
            fmt.Println("Stack trace:")
            debug.PrintStack()
        }
    }()
    
    var nilMap map[string]string
    nilMap["key"] = "value" // Will panic
}

func main() {
    riskyOperation()
    fmt.Println("Program continues after recovery")
}

恢复处理程序会记录 panic 值和堆栈跟踪。这有助于调试,同时允许程序继续运行。

在 Web 服务器中恢复

Web 服务器应从 panic 中恢复以保持可用性。此示例展示了 HTTP 处理程序中的 panic 恢复。

http_recover.go
package main

import (
    "fmt"
    "net/http"
)

func panicHandler(w http.ResponseWriter, r *http.Request) {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in handler:", r)
            w.WriteHeader(http.StatusInternalServerError)
            fmt.Fprintf(w, "Internal server error")
        }
    }()
    
    panic("handler panic")
}

func main() {
    http.HandleFunc("/", panicHandler)
    fmt.Println("Server started on :8080")
    http.ListenAndServe(":8080", nil)
}

HTTP 处理程序可以从 panic 中恢复并返回适当的错误响应。这可以防止整个服务器因单个请求而崩溃。

来源

Go 语言规范

本教程通过在各种场景下进行 panic 恢复的实际示例,介绍了 Go 中的 recover 函数。

作者

我的名字是 Jan Bodnar,我是一名充满热情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。迄今为止,我已撰写了 1400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。

列出所有 Golang 教程