Go Academy · 课时

使用 errgroup 处理并发错误

实践 golang.org/x/sync/errgroup

第 2 / 4 课13 个步骤

使用 errgroup 处理并发错误 是 CoddyKit 上的免费 Go Academy 课时。 这是第 2 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Go Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Go Academy 课程共包含 4 节课。

goroutine 和错误的问题

使用 go func() 启动 goroutine 会丢弃包括错误在内的返回值。您需要借助额外机制(通道、WaitGroup)来收集这些返回值。errgroup 可以简化这一过程。

golang.org/x/sync/errgroup

errgroup.Group 会运行 goroutine 并收集错误。Wait() 会阻塞,直到所有 goroutine 完成,然后返回第一个非 nil 错误。

var g errgroup.Group
g.Go(func() error { return fetchUsers() })
g.Go(func() error { return fetchOrders() })
if err := g.Wait(); err != nil {
    log.Fatal(err)
}

errgroup.WithContext

WithContext 会返回一个组和一个派生上下文。当第一个 goroutine 返回错误时,上下文会被取消,从而停止剩余工作。

g, ctx := errgroup.WithContext(context.Background())
g.Go(func() error { return doWork(ctx) })
g.Go(func() error { return doOtherWork(ctx) })
err := g.Wait()

同时收集结果和错误

errgroup 只收集错误,不收集结果。请将结果存入预先分配的切片,并通过索引访问,以避免同步:

results := make([]Result, len(items))
for i, item := range items {
    i, item := i, item
    g.Go(func() error {
        r, err := process(ctx, item)
        if err != nil { return err }
        results[i] = r
        return nil
    })
}
if err := g.Wait(); err != nil { return err }

g.SetLimit(Go 1.20+)

g.SetLimit(n) 会限制并发运行的 goroutine 数量——无需单独的通道即可充当内联信号量。

g.SetLimit(10) // at most 10 goroutines at once
for _, item := range items {
    g.Go(func() error { return process(item) })
}

g.TryGo

g.TryGo(f) 仅在 SetLimit 设置的限制允许时启动 goroutine。如果未启动,则返回 false。

只返回第一个错误

errgroup 只返回第一个错误。如果需要收集所有错误,请在每个 goroutine 内将错误收集到由互斥锁保护的切片中。

var mu sync.Mutex
var errs []error
g.Go(func() error {
    if err := doWork(); err != nil {
        mu.Lock(); errs = append(errs, err); mu.Unlock()
    }
    return nil
})

与 sync.WaitGroup 的区别

WaitGroup 跟踪完成状态;errgroup 不仅跟踪完成状态,还会收集错误。当任何 goroutine 的失败都应被报告时,请优先使用 errgroup。

上下文取消传播

使用 errgroup.WithContext 时,派生上下文的取消信号会通知所有其他 goroutine 停止——这类似于发生第一个错误时取消的语义。

测试 errgroup 代码

在测试中,请使用 WithTimeout 创建上下文,以确保某个 goroutine 挂起时该组不会无限运行。

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
g, ctx := errgroup.WithContext(ctx)

信号量替代方案

在 SetLimit 出现之前(Go < 1.20),可以在各个 goroutine 中使用带缓冲的通道作为信号量,以实现相同效果。

快速检查

errgroup.WithContext 会返回什么,而普通的 errgroup.Group 不会返回什么?

回顾:errgroup

要点:

  • g.Go(func() error)——启动 goroutine 并收集第一个错误
  • g.Wait()——阻塞直到全部完成;返回第一个错误
  • WithContext——在第一个错误发生时取消上下文
  • g.SetLimit(n)——限制并发 goroutine 数量(Go 1.20+)
免费开始

用 AI 导师学习 Go — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
51
课程
203

常见问题解答

「使用 errgroup 处理并发错误」课时是免费的吗?

是的 — 「使用 errgroup 处理并发错误」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Go Academy 课程的其余内容,请升级到 CoddyKit PRO。 Go Academy 课程共包含 4 节课。

「使用 errgroup 处理并发错误」这节课中我会学到什么?

实践 golang.org/x/sync/errgroup 你通过在浏览器中直接运行的动手代码来练习 Go Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Go Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Go Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 2 节课,共 4 节。

「使用 errgroup 处理并发错误」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Go Academy 课中编写并运行代码吗?

能。每节 Go Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 流水线与阶段模式
  2. 使用 errgroup 处理并发错误
  3. 信号量模式
  4. 泄漏检测与取消
← 返回 Go Academy