goroutine内panic无法被外层recover捕获,必须在每个goroutine内部用defer/recover处理;recover后应立即return或通过channel通知主goroutine,避免状态不一致。
goroutine里panic不会被外层recover捕获
Go的
只在当前goroutine内传播,主goroutine调用
对其他goroutine里的panic完全无效。这是最常踩的坑——以为在main函数里包一层
就能兜住所有并发异常。
实际必须在每个可能出错的goroutine内部单独做
,否则panic会直接终止该goroutine(不崩溃进程,但可能丢任务、漏日志)。
错误写法:
外面包
正确写法:每个goroutine自己处理,例如
注意: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:
需要反馈结果时,用带缓冲channel避免阻塞:
切勿在recover后继续使用已panic路径中可能损坏的变量(如部分初始化的struct、未关闭的file等)
使用errgroup或sync.WaitGroup时panic处理要同步
用
或
管理并发时,某个goroutine panic会导致等待逻辑卡死或结果丢失——因为panic的goroutine没走完,WaitGroup计数没减,或者errgroup没收到error。
errgroup:必须确保每个子goroutine都把error发给
包装的函数,否则panic会被吞掉;推荐用
而非裸
启动
WaitGroup:在recover后务必调用
,否则main goroutine永远阻塞在
示例片段:
log.Fatal和os.Exit在goroutine里要特别小心
和
不是panic,它们会直接终止整个进程,不受任何recover影响。如果在goroutine里误用,会导致整个服务宕机,比panic更危险。
并发场景下必须用
或自定义error channel上报,而不是
:
禁止:
允许:
若需全局退出(如配置加载失败),应由主goroutine统一判断,而非任一worker自行调用
recover本身不能跨goroutine起作用,而log.Fatal这类进程级退出在并发里又极易误用——这两点是线上Go服务因并发panic导致雪崩的最常见根源。
panicrecoverdefer recover()defer/recovergo func() { /* 可能panic的代码 */ }()defer recover()go func() { defer func() { if r := recover(); r != nil { log.Println("panic:", r) } }(); /* 业务逻辑 */ }()defer func() { if r := recover(); r != nil { log.Printf("worker panic: %v", r); return } }()done := make(chan error, 1); go func() { defer func() { if r := recover(); r != nil { done errgroup.Groupsync.WaitGroupg.Go()g.Go(func() error { ... })gowg.Done()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.Fatalos.Exitlog.Printflog.Fatalgo func() { if err != nil { log.Fatal(err) } }()go func() { if err != nil { log.Printf("worker error: %v", err); return } }()os.Exit