panic不能跨goroutine传播,因goroutine相互隔离且无隐式错误冒泡机制;需通过带缓冲的chan error等显式通信方式传递错误,并确保发送与接收配对。

为什么 panic 不能跨 goroutine 传播
Go 的 panic 只在当前 goroutine 内生效,一旦在子 goroutine 中触发,不会自动中断父 goroutine 或其他协程。这是设计使然——goroutine 是隔离的执行单元,没有隐式错误冒泡机制。常见现象是:子 goroutine panic 后程序没崩溃,但错误被静默吞掉,主流程继续运行,结果数据异常或逻辑错乱。
真正要传递错误,得靠显式通信。核心思路只有两个:channel 或 error 返回值 + 同步等待(如 sync.WaitGroup 或 context)。别指望语言替你做跨协程错误转发。
用 chan error 接收单个 goroutine 的错误
最轻量、最可控的方式:为每个启动的 goroutine 配一个 chan error,并在其结束时发送错误(或 nil)。主 goroutine 用 select 或直接接收来判断是否出错。
- 必须用带缓冲的 channel(如
make(chan error, 1)),否则 goroutine 可能因发送阻塞而卡死 - 务必确保 goroutine 结束前一定发一次 error(哪怕为
nil),否则主 goroutine 接收会永久阻塞 - 不要在 goroutine 内部 recover 后忽略 error——recover 只是拦截 panic,不等于错误已处理
errgroup.Group
- 默认行为是“第一个非 nil error 返回”,后续 goroutine 会被取消(需配合
ctx使用) - 若想等全部完成再看错误,调用
eg.Wait()后检查eg.Err();但注意这不返回所有错误,只返回第一个 - 不要混用多个
errgroup.Group实例去管理同一组 goroutine,会导致 wait 逻辑错乱
context.Context
- 永远用
ctx.WithCancel/ctx.WithTimeout创建子 context,不要传入context.Background()后再 cancel - 在 goroutine 内部,
select中必须包含case 分支 - 不要在 goroutine 里调用
cancel()——那是发起方的责任;子 goroutine 只负责响应
跨协程错误传递的本质,是把“错误发生”这个事件,转化为可监听、可响应、可组合的控制流信号。越早把 error 和 context、channel 绑定在一起设计,后期越不容易掉进静默失败的坑。











