recover 只能在引发 panic 的同一 goroutine 的 defer 函数中调用才有效;跨 goroutine 无法捕获,主 goroutine panic 会终止程序,子 goroutine panic 默认静默退出。
Go 的 recover 只能在 defer 中、且必须在引发 panic 的同一 goroutine 内调用才有效——跨 goroutine 的 panic 无法被其他 goroutine 的 recover 捕获。
这是最常踩的坑:启动一个新 goroutine 执行可能 panic 的逻辑,却在主 goroutine 里 defer + recover,结果毫无作用。
recover() 必须出现在 panic 发生的同一个 goroutine 的 defer 函数中GODEBUG=panicnil=1 等调试标志)go func() {
defer func() {
if r := recover(); r != nil {
log.Printf("goroutine recovered: %v", r)
}
}()
panic("boom in goroutine")
}()
安全模式是:每个可能 panic 的 goroutine 自己负责 recover,而不是依赖外部统一捕获。
defer recover 逻辑写进 goroutine 内部,或封装为可复用函数如 safeGo
func safeGo(f func()) {
go func() {
defer func() {
if r := recover(); r != nil {
log.Printf("panic recovered in goroutine: %v", r)
// 可选:上报指标、触发告警、写入错误追踪 ID
}
}()
f()
}()
}
// 使用
safeGo(func() {
doRiskyWork() // 可能 panic
})
recover 只对 panic 有效,对运行时致命错误(如 nil pointer dereference、stack overflow、out of memory)无效——这些会直接终止程序。
立即学习“go语言免费学习笔记(深入)”;
recover 可捕获runtime.SetMaxStack 过小导致栈溢出,或 cgo 调用中发生 segfault,则无法 recoveros.Exit()、syscall.Exit()、向已关闭 channel 发送值(非 panic 场景)等也不受 recover 影响recover 只解决“不崩溃”,但不解决“任务是否完成”“下游是否等待”等问题。真实场景中需配合 context 做协同退出。
ctx.Done() 通知上游放弃等待func worker(ctx context.Context, ch <-chan int) {
defer func() {
if r := recover(); r != nil {
log.Printf("worker panicked: %v", r)
// 注意:此处不能直接 return,需确保资源清理
// 如:close(outChan), mu.Unlock(), file.Close()
}
}()
for {
select {
case <-ctx.Done():
return
case n := <-ch:
process(n) // 可能 panic
}
}
}
真正难的不是写 recover,而是判断 panic 后的状态是否可信、要不要重试、下游能否容忍部分失败——这些没法靠语法机制自动解决。