跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

Go语言并发下如何处理panic_Golang并发异常恢复

goroutine内panic无法被外层recover捕获,必须在每个goroutine内部用defer/recover处理;recover后应立即return或通过channel通知主goroutine,避免状态不一致。 goroutine里panic不会被外层recover捕获 Go的
panic
只在当前goroutine内传播,主goroutine调用
recover
对其他goroutine里的panic完全无效。这是最常踩的坑——以为在main函数里包一层
defer recover()
就能兜住所有并发异常。 实际必须在每个可能出错的goroutine内部单独做
defer/recover
,否则panic会直接终止该goroutine(不崩溃进程,但可能丢任务、漏日志)。 错误写法:
go func() { /* 可能panic的代码 */ }()
外面包
defer recover()
正确写法:每个goroutine自己处理,例如
go func() { defer func() { if r := recover(); r != nil { log.Println("panic:", r) } }(); /* 业务逻辑 */ }()
注意:recover只能捕获同一goroutine内、且尚未返回的panic;一旦goroutine已退出,recover无意义 recover后如何安全退出goroutine recover只是阻止panic向上冒泡,不代表goroutine能继续执行后续逻辑。尤其涉及资源释放、状态更新时,盲目继续可能引发数据竞争或状态不一致。 典型做法是recover后立即return,或通过channel通知主goroutine该任务失败: 立即学习 “ go语言免费学习笔记(深入) ”; go语言参考手册 中文CHM版 Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文给大家带来Go参考手册,需要的可以来下载! Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、v数组安全、编译 下载 简单场景直接return:
defer func() { if r := recover(); r != nil { log.Printf("worker panic: %v", r); return } }()
需要反馈结果时,用带缓冲channel避免阻塞:
done := make(chan error, 1); go func() { defer func() { if r := recover(); r != nil { done
切勿在recover后继续使用已panic路径中可能损坏的变量(如部分初始化的struct、未关闭的file等) 使用errgroup或sync.WaitGroup时panic处理要同步 用
errgroup.Group
或
sync.WaitGroup
管理并发时,某个goroutine panic会导致等待逻辑卡死或结果丢失——因为panic的goroutine没走完,WaitGroup计数没减,或者errgroup没收到error。 errgroup:必须确保每个子goroutine都把error发给
g.Go()
包装的函数,否则panic会被吞掉;推荐用
g.Go(func() error { ... })
而非裸
go
启动 WaitGroup:在recover后务必调用
wg.Done()
,否则main goroutine永远阻塞在
wg.Wait()
示例片段:
go func() { defer wg.Done(); defer func() { if r := recover(); r != nil { mu.Lock(); err = fmt.Errorf("panic: %v", r); mu.Unlock() } }(); /* 业务 */ }()
log.Fatal和os.Exit在goroutine里要特别小心
log.Fatal
和
os.Exit
不是panic,它们会直接终止整个进程,不受任何recover影响。如果在goroutine里误用,会导致整个服务宕机,比panic更危险。 并发场景下必须用
log.Printf
或自定义error channel上报,而不是
log.Fatal
: 禁止:
go func() { if err != nil { log.Fatal(err) } }()
允许:
go func() { if err != nil { log.Printf("worker error: %v", err); return } }()
若需全局退出(如配置加载失败),应由主goroutine统一判断,而非任一worker自行调用
os.Exit
recover本身不能跨goroutine起作用,而log.Fatal这类进程级退出在并发里又极易误用——这两点是线上Go服务因并发panic导致雪崩的最常见根源。

相关文章