当前位置:首页 > 文章列表 > Golang > Go问答 > Go recover 为什么捕获不到另一个协程的 panic

Go recover 为什么捕获不到另一个协程的 panic

来源:17golang原创 2026-09-05 21:58:48 0浏览 收藏

很多 Go 程序第一次给并发任务加兜底时,会把 defer recover() 写在 main 或父函数里,然后发现 worker 里的 panic 仍然让程序退出。原因不是 recover 失效,而是它有严格的 goroutine 作用域:go worker() 建立了独立的控制栈,父 goroutine 的 defer 不会跨过去。要接住这个 panic,必须在 worker 入口安装一个直接调用 recover 的 deferred function。

记住一句话:panic 在哪条 goroutine 栈上发生,recover 就要在同一条栈的 deferred function 里调用;跨 goroutine 只能通过 channel、返回值或其他协作方式传递处理结果。
要点速览
  • 父函数的 recover 只能处理当前 goroutine 栈上的 panic。
  • worker 应由包装器统一 defer recover,并把 panic 值转换为 error。
  • 用 error channel 汇总异常,用 WaitGroup 负责生命周期,避免主流程提前退出或关闭通道过早。

先用一个最小例子看清作用域

下面的代码故意把 recover 放在父函数中。它只能保护 main 自己调用的同步代码,保护不了新 goroutine:

func main() {
    defer func() {
        fmt.Println("parent recover:", recover())
    }()

    go func() {
        panic("worker failed")
    }()
    time.Sleep(100 * time.Millisecond)
}

即使父函数还没有返回,worker 也已经在自己的控制栈上 panic。规范描述的恢复条件是“同一 goroutine 中发生 panic”,所以父函数的 deferred function 没有机会接管它。真实服务里不要用 sleep 等待结果,这里只用于暴露问题。

Go recover 同一 goroutine 作用域中主 goroutine、worker panic 与 deferred recover 的静态边界关系
图1:主 goroutine 与 worker goroutine 是两条独立控制栈,recover 只能落在发生 panic 的 worker 边界内。

把 recover 放进 worker 包装器

更稳妥的做法是规定:所有由业务代码启动的 worker,都从同一个包装器进入。recover 必须由这个包装器直接调用,不能再绕一层普通函数。

func safeGo(wg *sync.WaitGroup, errs chan

这里两个 defer 按后进先出执行:恢复逻辑先运行,随后 wg.Done() 标记 worker 结束。panic 被恢复后,safeGo 会正常返回,调用方收到的是一个普通 error。若 panic 值需要保留类型,可把 fmt.Errorf 换成项目自己的错误结构;不要把堆栈和敏感数据无条件写入日志。

Go safeGo 包装器将 worker panic 通过直接 deferred recover 转成 error channel 结果并由主流程验收
图2:safeGo 将 worker 的 panic 约束在自身 goroutine 内,再通过 error channel 和 WaitGroup 交给主流程验收。

用 channel 和 WaitGroup 汇总一个可验收的小项目

把包装器接到主流程时,关键是先登记 worker,再等待全部 worker 结束,最后关闭结果 channel:

func main() {
    var wg sync.WaitGroup
    errs := make(chan error, 2)

    jobs := []func(){
        func() { fmt.Println("job A ok") },
        func() { panic("job B failed") },
    }
    wg.Add(len(jobs))
    for _, job := range jobs {
        go safeGo(&wg, errs, job)
    }

    go func() {
        wg.Wait()
        close(errs)
    }()

    for err := range errs {
        fmt.Println("received:", err)
    }
    fmt.Println("all workers checked")
}

errs 设为有缓冲 channel,是为了让最后一个 worker 在收集循环尚未调度时也能交付错误;缓冲数量应按并发策略设计,不能当成无限队列。只有等待所有发送者结束后才能 close(errs),否则会出现 “send on closed channel”。验收标准也很具体:能看到 worker panic: job B failed,随后输出 all workers checked,进程不会因该业务 panic 直接终止。

三个容易让 recover 再次失效的边界

现象原因处理
父函数 recover 没反应panic 在另一个 goroutine把 defer recover 放到 worker 包装器
recover 返回 nil不在 panic 状态,或不是 deferred function 直接调用检查调用位置,避免在 helper 中间接调用
程序不退出但任务结果丢失只恢复了 panic,没有传递异常转为 error,通过 channel 或结果对象汇总

还要区分“可恢复的业务边界”和“程序不变量被破坏”。对单个任务输入异常,可以恢复并返回 error;共享状态损坏、严重运行时错误或无法保证继续工作的情况,不应仅靠 recover 掩盖。Go 1.21 起,直接调用 panic(nil) 也会触发非 nil 的运行时 panic,代码仍应把 recover 当作最后一道边界,而不是日常错误返回的替代品。

相关问题

recover 能不能在另一个函数里调用?

可以调用,但只有它本身就是直接被 defer 的函数时,才具备恢复语义。若 defer 的函数先调用 helper,再由 helper 调 recover,通常会得到 nil。

每个 goroutine 都要写一遍 recover 吗?

不必复制代码,把启动入口统一收口到 safeGo 之类的包装器即可。无法控制的第三方 goroutine 不能由你的父函数代为恢复。

recover 后还需要返回 error 吗?

需要。recover 只改变 panic 的控制流,不会自动告诉业务方任务失败;通过 error channel、结果结构或回调传递,主流程才能记录、重试或降级。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Redis 排行榜分数相同时怎么安排排序Redis 排行榜分数相同时怎么安排排序
上一篇
Redis 排行榜分数相同时怎么安排排序
Go 查询数据库遇到 NULL 怎么接收
下一篇
Go 查询数据库遇到 NULL 怎么接收
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • SuperCLUE中文大模型评测基准:功能、能力维度与应用指南
    SuperCLUE
    SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
    152次使用
  • C-Eval中文评测基准:大语言模型多学科能力评估指南
    C-Eval
    深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
    84次使用
  • ClickPrompt:AI提示词生成与优化工具,支持Stable Diffusion、ChatGPT及代码辅助
    ClickPrompt
    ClickPrompt是一款专为AI提示词编写者设计的开源在线工具,支持Stable Diffusion绘图、ChatGPT对话及GitHub Copilot代码辅助。提供Prompt自动生成、一键运行、社区分享及可视化优化功能,帮助用户高效获取精准AI输出。
    42次使用
  • PromptHero官网:AI提示词搜索、优化与学习平台,支持Midjourney/Stable Diffusion
    PromptHero
    PromptHero是专业的AI提示词搜索引擎与优化平台,支持Stable Diffusion、Midjourney等主流模型。提供海量提示词库、分类搜索、在线课程及社区互动,助力用户高效生成高质量AI图像与文本。
    25次使用
  • OpenArt免费开源指南:Stable Diffusion Prompt Book提示词手册详解
    Stable Diffusion Prompt Book
    深入解析OpenArt推出的Stable Diffusion Prompt Book,这本免费的开源提示词指南涵盖从基础语法到高级技巧,提供风格化词库与参数建议,助您优化AI绘画生成效果。
    28次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码