goroutine错误无法直接返回主函数,需用errgroup.Group、channel+WaitGroup或context控制;errgroup默认返回首个错误,全量收集需自定义channel;超时须配合context避免阻塞。

goroutine 中的错误无法直接返回给主函数
Go 的 goroutine 是独立执行的,一旦启动就脱离调用栈,return 值或 panic 都不会传回主 goroutine。常见错误是写成这样:
func doWork() error {
go func() {
// 模拟失败
panic("failed in goroutine")
}()
return nil // 主函数收不到 panic,也收不到子 goroutine 的 error
}这会导致错误被静默吞掉。必须显式传递错误——用 channel、sync.WaitGroup 配合 error 类型变量,或用 errgroup.Group。
用 errgroup.Group 统一收集并发错误
errgroup.Group 是 Go 官方 golang.org/x/sync/errgroup 提供的工具,能自动等待所有 goroutine 结束,并返回第一个非 nil 错误(或 nil 表示全部成功)。
- 它内部封装了
sync.WaitGroup和带缓冲的errorchannel - 支持上下文取消:
eg.Go(ctx, fn)会在ctx取消时自动中止未启动的任务 - 注意:默认只返回「第一个」错误;如需收集全部错误,得自己改用
chan error
import "golang.org/x/sync/errgroup"func processAll(urls []string) error { var eg errgroup.Group for , url := range urls { url := url // 避免循环变量复用 eg.Go(func() error { , err := http.Get(url) return err }) } return eg.Wait() // 阻塞直到全部完成,返回首个 error }
手动用 channel + WaitGroup 收集多个错误
当需要保留所有错误(比如做批量操作审计),就不能依赖 errgroup 的“首个错误”语义,得自己建 chan error 并控制容量。
立即学习“go语言免费学习笔记(深入)”;
- channel 必须有足够缓冲(如
make(chan error, len(tasks))),否则发送会阻塞 - 记得在每个 goroutine 里 recover panic,否则 panic 会让整个程序崩溃
-
WaitGroup的Done()要在 defer 里调用,确保即使 panic 也执行
func runWithAllErrors(tasks []func() error) []error {
var wg sync.WaitGroup
errs := make(chan error, len(tasks))
for _, task := range tasks {
wg.Add(1)
go func(t func() error) {
defer wg.Done()
if r := recover(); r != nil {
errs <- fmt.Errorf("panic: %v", r)
return
}
if err := t(); err != nil {
errs <- err
}
}(task)
}
wg.Wait()
close(errs)
var result []error
for err := range errs {
result = append(result, err)
}
return result}
context.WithTimeout 配合错误收集防无限等待
并发任务若不设超时,一个卡死的 goroutine 会让 WaitGroup.Wait() 或 errgroup.Wait() 永远阻塞。必须结合 context 控制生命周期。
-
errgroup.Group的Go方法支持传入context.Context,任务函数可监听ctx.Done() - 手动 channel 方案中,应在每个 goroutine 内 select 监听
ctx.Done(),及时退出 - 超时后,
ctx.Err()是context.DeadlineExceeded,应作为错误之一返回
容易忽略的是:超时 context 取消后,正在运行的 goroutine 不会自动停止,必须主动检查 ctx.Err() == nil 才继续执行关键逻辑。










