编程高级 Go 开发者

请讲述一下 Go 如何实现内置的 recover 和 panic 函数。它们在 goroutine 中安全恢复的正确使用顺序是什么?

用 Hintsage AI 助手通过面试

答案

在 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!") // 不会导致程序停止 })

重要的是要记住:

  • panic 会结束当前 goroutine 的执行,直到第一个 recover
  • recover 仅在同一 goroutine 的 defer 调用中有效
  • 如果不使用 recover,panic 会导致整个程序崩溃

带有陷阱的问题

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 中工作。结果,当队列中出现不正确的消息时,服务不定期崩溃。