Go recover 为什么接不住另一个 goroutine 的 panic
因为 recover 处理的是当前 goroutine正在展开的 panic,而不是整个 Go 进程里的任意 panic。父 goroutine 里写了 defer recover(),并不会替子 goroutine 接管调用栈;真正启动 worker 的位置,必须在 worker 自己的入口注册恢复逻辑。
go fn()会建立独立的 goroutine 调用栈,panic 只沿这条栈向上展开。recover必须由 deferred function 直接调用,并且该 defer 位于发生 panic 的同一 goroutine。- 生产代码通常在 worker 边界把 panic 转成带上下文的 error,再由父 goroutine 决定记录、重试或停止。
panic 到底沿哪条调用栈展开
Go 规范把 go 语句描述为启动一条独立的并发控制流。普通函数调用仍在当前 goroutine 的栈上继续,go worker() 则把 worker 放到另一条栈上执行。panic 发生后,会执行这条栈上的 deferred function,并一直向该 goroutine 的顶层函数展开;找不到恢复点时,进程会终止并打印 panic 信息。
可以把边界记成一句话:谁的栈发生 panic,谁的 defer 才有机会 recover。这不是“父子任务”关系,也不是按 OS 线程编号匹配;goroutine 之间没有共享的异常捕获链。

为什么父 goroutine 的 defer 接不住子 goroutine
下面的写法看起来像给整个任务加了保护,但 recover 所在的 defer 属于 main 的 goroutine。子 goroutine 中的数组越界发生在另一条调用栈上,父栈既不会同步展开,也没有机会执行这个 recover。
package main
import "fmt"
func main() {
defer func() {
// 这个 defer 只保护 main 所在的 goroutine。
if v := recover(); v != nil {
fmt.Println("main recovered:", v)
}
}()
go func() {
// panic 发生在新的 goroutine,调用栈与 main 已经分开。
panic("worker failed")
}()
}
问题不在于 recover 写错了位置,而在于它和 panic 不在同一条栈上。对一个异步 worker,父 goroutine 应该等待一个结果通道或 sync.WaitGroup,但恢复动作仍然要放进 worker 的函数体。
正确做法:在 worker 入口建立恢复边界
将 worker 统一包成一个函数,先注册 defer,再执行真实任务。恢复后不要只打印字符串:应该保留 worker 名称、panic 值和必要的堆栈信息,然后通过带缓冲的 channel 把错误交给调用方。这样父 goroutine 能继续按业务策略处理,而不是误以为 panic 已经被全局捕获。
package main import ( "fmt" "runtime/debug" ) func runWorker(name string, work func())
这个包装器的关键不是“把 panic 变成普通错误”这么简单,而是把恢复范围固定在 worker 边界:worker 自己完成 defer、堆栈采集和错误发送,父 goroutine 只消费结果。若 worker 还持有文件、锁或连接,资源释放也应在同一 goroutine 里用其他 defer 注册。

recover 的位置、结果与边界
| 写法或位置 | 结果 | 处理建议 |
|---|---|---|
| 普通函数中直接调用 recover | 通常得到 nil,不能停止 panic 展开 | 改到同一 goroutine 的 deferred function |
| 父 goroutine 的 defer | 接不到子 goroutine 的 panic | 在每个异步入口单独包裹 |
| worker 自己的 deferred function | 可拿到 panic 值并停止当前展开 | 补充日志、堆栈和错误回传 |
| 恢复后继续运行 | panic 点之后的当前函数不会自动续跑 | 让包装器返回结果,由上层决定下一步 |
不要把 recover 当作所有故障的替代品。输入不满足预期、网络失败和业务拒绝更适合显式返回 error;只有确实需要隔离边界的 worker、插件或批处理任务,才值得在入口恢复。对程序不变量被破坏、无法安全继续的 panic,记录上下文后让进程退出往往比静默吞掉更可靠。
常见问题
父 goroutine 能不能通过 channel recover 子 goroutine?
不能。channel 只能传递值或错误,不能把另一条 goroutine 的调用栈转移过来;应让子 goroutine 自己 recover,再发送错误。
defer recover 写在 worker 调用之后可以吗?
不可以。defer 必须在可能 panic 的代码执行前注册,而且 recover 要由 deferred function 直接调用。
recover 后 worker 会从 panic 那一行继续吗?
不会。恢复会停止当前 panic 展开并结束相关函数的执行;包装器应通过返回值或 channel 明确告诉调用方任务没有正常完成。
排查这类问题时,先标出 panic 实际发生的 goroutine,再沿它的入口向上找 defer。只要恢复逻辑和 panic 位于同一条调用栈,问题通常就从“recover 为什么失效”变成了可验证的边界设计。
Go flag 包怎样判断参数是否真的传入
- 上一篇
- Go flag 包怎样判断参数是否真的传入
- 下一篇
- Go concurrent map read and map write 崩溃怎么改
-
- Golang · Go问答 | 19分钟前 | 标准库 · 错误处理 · go · IO · Go io.MultiWriter io.Writer ErrShortWrite 多目标写入
- Go io.MultiWriter 写入失败时怎样判断部分结果
- 434浏览 收藏
-
- Golang · Go问答 | 22分钟前 |
- Go Ticker Reset 期间为什么仍然收到旧节拍
- 173浏览 收藏
-
- Golang · Go问答 | 25分钟前 | 网络编程 · go · IPv6 Go ipv4 netip AddrFromSlice
- Go netip.AddrFromSlice 处理 IPv4 映射 IPv6 的转换边界
- 282浏览 收藏
-
- Golang · Go问答 | 31分钟前 |
- Go errors.Is 怎样判断包装错误是否属于目标错误
- 208浏览 收藏
-
- Golang · Go问答 | 34分钟前 | range select Go Channel关闭 nil channel
- Go range nil channel 永远阻塞怎样定位
- 107浏览 收藏
-
- Golang · Go问答 | 39分钟前 |
- Go WaitGroup Add 放在 Wait 后面为什么不可靠
- 303浏览 收藏
-
- Golang · Go问答 | 43分钟前 | Go Mutex 数据竞争 共享状态 go test -race
- Go 数据竞争测试偶现失败怎样缩小范围
- 335浏览 收藏
-
- Golang · Go问答 | 43分钟前 |
- Go concurrent map read and map write 崩溃怎么改
- 375浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 568次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 614次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 641次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 600次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 418次使用
-
- Go map 并发写 panic 怎么办:从共享 map 到可控写入路径
- 2026-06-30 123浏览
-
- golang中的defer函数理解
- 2022-12-22 366浏览
-
- 一文搞懂Go语言中defer关键字的使用
- 2022-12-30 485浏览
-
- GolangDefer基础操作详解
- 2022-12-22 444浏览
-
- 详解golang中的闭包与defer
- 2023-01-07 461浏览

