You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用errgroup捕获runner.go中run函数持续运行时产生的错误

问题排查

你当前无法捕获run函数错误的核心原因是:

  • Start函数中调用run(ctx)时直接丢弃了返回的错误,没有将错误向上传递给errgroup托管的goroutine
  • errgroup只有当绑定的goroutine返回非nil错误时才会捕获到错误,你当前Start函数除了ctx退出时返回nil,其余场景永远不会返回值,自然无法把run的错误抛给上层。

实现方案

分两种常见场景实现,你可以按需选择:

场景1:单次run报错就终止整个运行流程

直接修改runner.go中的Start函数,捕获到run的错误后直接向上返回即可:

func Start(ctx context.Context, dur time.Duration) error {
    ticker := time.NewTicker(dur)
    defer ticker.Stop()

    for {
        select {
        case <-ctx.Done():
            return nil
        case <-ticker.C:
            // 接收run的返回值,非nil则向上传递
            if err := run(ctx); err != nil {
                return err
            }
        }
    }
}

修改后运行逻辑:
第一次定时触发时run返回错误,Start把错误返回给errgroup的执行函数,errgroup自动取消关联的ctx,g.Wait()会返回该错误,你可以直接在主函数中捕获打印。


场景2:单次run报错不终止,持续运行同时捕获所有错误

如果你需要让定时任务持续运行,只需要记录每次run的错误,不要因为单次报错就停掉整个流程,可以用错误回调或者通道传递错误:

调整main.go代码

package main

import (
    "context"
    "fmt"
    "awesomeProject/runner"
    "golang.org/x/sync/errgroup"
    "time"
)

func main() {
    ctx := context.Background()
    g, ctx := errgroup.WithContext(ctx)
    // 初始化错误通道接收所有run的错误
    errChan := make(chan error, 10)

    g.Go(func() (err error) {
        // 传入错误处理回调
        return runner.Start(ctx, 1 * time.Second, func(e error) {
            errChan <- e
        })
    })

    // 单独启动goroutine处理捕获到的错误
    go func() {
        for e := range errChan {
            fmt.Printf("捕获到run函数错误: %v\n", e)
        }
    }()

    if err := g.Wait(); err != nil {
        fmt.Println("服务运行错误:", err)
    }
    close(errChan)
    fmt.Println("app terminated")
}

调整runner.go代码

package runner

import (
    "context"
    "errors"
    "fmt"
    "time"
)

// 新增errHandler参数用于传递错误
func Start(ctx context.Context, dur time.Duration, errHandler func(error)) error {
    ticker := time.NewTicker(dur)
    defer ticker.Stop()

    for {
        select {
        case <-ctx.Done():
            return nil
        case <-ticker.C:
            if err := run(ctx); err != nil {
                // 把错误抛给上层回调处理
                errHandler(err)
            }
        }
    }
}

func run(ctx context.Context) error {
    fmt.Println(time.Now().String())
    return errors.New("run:error")
}

修改后每次run报错都会通过回调传递给错误处理逻辑,定时任务会持续运行不会终止。


内容的提问来源于stack exchange,提问作者Serhat Morkoç

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 01:39:02