如何从 panic 中恢复 ?
参考回答
在 Go 中,可以使用内置的 recover 函数从 panic 中恢复。recover 必须在 defer 中调用,它捕获到的返回值是触发 panic 时传递的值。如果没有发生 panic,recover 返回 nil。
以下是一个简单示例:
package main
import "fmt"
func main() {
defer func() {
if r := recover(); r != nil {
fmt.Println("Recovered from panic:", r)
}
}()
fmt.Println("Before panic")
panic("something went wrong")
fmt.Println("After panic") // 不会执行
}
输出:
Before panic
Recovered from panic: something went wrong
详细讲解与拓展
panic和recover的工作机制panic:用于中断程序正常的执行流。它会引发运行时错误或手动触发的一种 “紧急状态”。recover:用于在程序崩溃时恢复执行,从而防止程序终止。
defer的作用
recover必须配合defer使用,因为panic触发后会立即退出当前函数,而defer中的代码会在退出前执行。示例:
func safeFunction() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered:", r) } }() fmt.Println("Executing risky operation") panic("operation failed") fmt.Println("This will not be printed") } func main() { safeFunction() fmt.Println("Program continues after recovery") }输出:
Executing risky operation Recovered: operation failed Program continues after recovery- 实际使用场景
- 防止程序崩溃: 用于服务端代码,防止单个请求的错误导致整个服务崩溃。
- 清理资源: 在
defer中执行recover时可以进行资源清理,例如关闭文件、释放锁等。 - 错误转换: 将
panic转为错误返回值,便于调用者处理。
示例:捕获
panic并返回错误func safeDivide(a, b int) (result int, err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf("panic occurred: %v", r) } }() result = a / b // 如果 b == 0,会触发 panic return } func main() { result, err := safeDivide(10, 0) if err != nil { fmt.Println("Error:", err) } else { fmt.Println("Result:", result) } }输出:
Error: panic occurred: runtime error: integer divide by zero - 需要注意的点
- 不可滥用
panic和recover:panic是用于严重错误的场景,不应该作为普通错误处理的工具。 recover只在当前的goroutine有效:如果panic发生在其他goroutine,recover无法捕获。
示例:
func main() { go func() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered in goroutine:", r) } }() panic("goroutine panic") }() // 主 goroutine 不受影响 fmt.Println("Main goroutine continues") } - 不可滥用
- 多层函数调用中的
panic
如果panic在深层函数中触发,但recover在高层函数中使用,它仍然能捕获到:func deep() { panic("deep panic") } func wrapper() { defer func() { if r := recover(); r != nil { fmt.Println("Recovered:", r) } }() deep() } func main() { wrapper() fmt.Println("Program continues after recovery") }输出:
Recovered: deep panic Program continues after recovery
总结
- 使用
recover从panic中恢复,必须配合defer使用。 - 典型场景包括防止程序崩溃、清理资源和错误转换。
recover应谨慎使用,避免掩盖真实错误,panic应保留给不可恢复的严重问题。- 注意
recover的作用范围仅限于当前goroutine。