在 Go 中,panic 用于指示程序中的致命错误。在调用 panic 后,执行开始从 unwind 栈并调用所有的 deferred 函数。如果栈中有调用 recover() 的函数,它可以捕获并处理 panic — 但 recover 仅在其 goroutine 内部工作,并且只能从 defer 调用。
安全恢复的推荐模式:
func safe(fn func()) { defer func() { if r := recover(); r != nil { fmt.Println("Recovered from panic:", r) } }() fn() } go safe(func() { panic("fail!") // 不会导致程序停止 })
重要的是要记住:
recover 能否捕获在其他 goroutine 中发生的 panic?
答案: 不,recover 仅在发生 panic 的那个 goroutine 内有效,并且只有在从 defer 函数调用时有效。如果 panic 在一个 goroutine 中发生,而 recover 在另一个 goroutine 中被调用 — 将无法捕获,程序将崩溃。
示例:
func main() { go func() { panic("inside goroutine") }() time.Sleep(time.Second) recover() // 不会生效!Panic 将结束程序 }
故事
开发者在主函数中通过模式 defer recover() 实现了错误处理。当 panic 在 worker 的独立 goroutine 中发生时,整个程序崩溃 — 错误未被全局 recover 捕获。
故事
在项目中,他们使用 defer/recover 来优雅地关闭 web 服务器,以为这样就足够了。结果发现,在一个 goroutine 中的 panic 结束了整个过程,因为 recover 的处理位置不正确 — 最终不得不重构整个 worker 池。
故事
在消息处理流中,由于用户函数的错误,发生了 panic。开发者期望顶层的 recover 能捕获它,但没有意识到 recover 只能从 defer 中工作。结果,当队列中出现不正确的消息时,服务不定期崩溃。