千家信息网

Go并发编程中sync/errGroup怎么使用

发表于:2026-11-02 作者:千家信息网编辑
千家信息网最后更新 2026年11月02日,本篇内容介绍了"Go并发编程中sync/errGroup怎么使用"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够
千家信息网最后更新 2026年11月02日Go并发编程中sync/errGroup怎么使用

本篇内容介绍了"Go并发编程中sync/errGroup怎么使用"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

    一.序

    这一篇算是并发编程的一个补充,起因是当前有个项目,大概の 需求是,根据kafka的分区(partition)数,创建同等数量的 消费者( goroutine)从不同的分区中消费者消费数据,但是总有某种原因导致,某一个分区消费者创建失败,但是其他分区消费者创建失败。 最初的逻辑是,忽略分区失败的逻辑,将成功创建的分区消费者收集,用于获取消息进行数据处理。 代码就不在这里展示。

    问题其实很明确: 如果在初始化分区消费者时,只要有一个消费创建失败,那么初始化工作就算失败,程序应该panic,退出。

    但是当初设计时,消费者负责从kafka上游的某个topic获取到数据,然后经过数据处理后,再通过生产者将处理后的数据发送到下游的topic中,由于当时设计时,代码耦合比较重,导致无法通过初始化工作做这些,只能在启动生产者后, 再创建消费者,这就导致 创建消费者-->获取数据-->处理数据 杂糅到了一起。 这个问题一直到最近才有时间想着来解决。

    比如有三个分区创建了三个分区的消费者,每个分区的消费者对应从自己的分区重获取数据,三个分区最初使用waitGroup进行控制三个分区创建,只有当三个分区都创建完成后才会执行后续逻辑。 但是 waitgroup并不能很好的解决:只要一个 goroutine 出错我们就不再等其他 goroutine 了,就默认创建分区消费者失败了,所以此时便想到了 errGroup

    二.errGroup

    errGroup 是google开源的基础扩展库。使用时先进行下载

    go get -u golang.org/x/sync

    2.1 函数签名

    type Group struct { // contains filtered or unexported fields}     func WithContext(ctx context.Context) (*Group, context.Context)    func (g *Group) Go(f func() error)    func (g *Group) Wait() error

    整个包就一个 Group 结构体

    • 通过WaitContext 可以创建一个带取消的group

    • Go 方法传入一个 func() error 内部会启动一个goroutine 去处理

    • Wait 类似WaitGroup的Wait 方法,等待所有的 goroutine结束后退出,返回的错误是一个出错的 err

    三.源码

    3.1 Group

    type Group struct {    // context 的 cancel 方法 cancel func()     // 复用 WaitGroup wg sync.WaitGroup  // 用来保证只会接受一次错误 errOnce sync.Once    // 保存第一个返回的错误 err     error}

    3.2 WaitContext

    func WithContext(ctx context.Context) (*Group, context.Context) { ctx, cancel := context.WithCancel(ctx) return &Group{cancel: cancel}, ctx}

    WithContext 就是使用 WithCancel 创建一个可以取消的 context 将 cancel 赋值给 Group 保存起来,然后再将 context 返回回去

    注意这里有一个坑,在后面的代码中不要把这个 ctx 当做父 context 又传给下游,因为 errgroup 取消了,这个 context 就没用了,会导致下游复用的时候出错

    3.3 Go

    func (g *Group) Go(f func() error) { g.wg.Add(1)  go func() {  defer g.wg.Done()                // 通过执行传入的匿名函数返回的错误值判断是否需要执行cancel  if err := f(); err != nil {      // 这一点很重要,确保错误只会被执行一次   g.errOnce.Do(func() {    g.err = err    if g.cancel != nil {     g.cancel()    }   })  } }()}

    Go 方法是一个封装,相当于go 关键字的加强,会启动一个携程,然后利用waitgroup 来控制是否结束,如果有一个非 nil 的 error 出现就会保存起来并且如果有 cancel 就会调用 cancel 取消掉,使 ctx 返回

    3.4 Wait

    func (g *Group) Wait() error { g.wg.Wait() if g.cancel != nil {  g.cancel() } return g.err}

    Wait 方法其实就是调用 WaitGroup 等待,如果有 cancel 就调用一下

    四. 案例

    基于 errgroup 实现一个 http server 的启动和关闭 ,以及 linux signal 信号的注册和处理,要保证能够 一个退出,全部注销退出。

    package main import ( "context" "fmt" "log" "net/http" "os" "os/signal" "syscall" "time"  "golang.org/x/sync/errgroup") func main() { g, ctx := errgroup.WithContext(context.Background())  mux := http.NewServeMux() mux.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {  _, _ = w.Write([]byte("pong")) })  // 模拟单个服务错误退出 serverOut := make(chan struct{}) mux.HandleFunc("/shutdown", func(w http.ResponseWriter, r *http.Request) {  serverOut <- struct{}{} })  server := http.Server{  Handler: mux,  Addr:    ":8099", }  // g1 // g1 退出了所有的协程都能退出么? // g1 退出后, context 将不再阻塞,g2, g3 都会随之退出 // 然后 main 函数中的 g.Wait() 退出,所有协程都会退出 g.Go(func() error {  err := server.ListenAndServe() // 服务启动后会阻塞, 虽然使用的是 go 启动,但是由于 g.WaitGroup 试得其是个阻塞的 协程  if err != nil {   log.Println("g1 error,will exit.", err.Error())  }  return err })  // g2 // g2 退出了所有的协程都能退出么? // 到调用 `/shutdown`接口时, serverOut 无缓冲管道写入数据, case接收到数据后执行server.shutdown, 此时 g1 httpServer会退出 // g1退出后,会返回error,将error加到g中,同时会调用 cancel() // g3 中会 select case ctx.Done, context 将不再阻塞,g3 会随之退出 // 然后 main 函数中的 g.Wait() 退出,所有协程都会退出 g.Go(func() error {  select {  case <-ctx.Done():   log.Println("g2 errgroup exit...")  case <-serverOut:   log.Println("g2, request `/shutdown`, server will out...")  }   timeoutCtx, cancel := context.WithTimeout(context.Background(), 3*time.Second)  // 这里不是必须的,但是如果使用 _ 的话静态扫描工具会报错,加上也无伤大雅  defer cancel()   err := server.Shutdown(timeoutCtx)  log.Println("shutting down server...")  return err })  // g3 // g3 捕获到 os 退出信号将会退出 // g3 退出了所有的协程都能退出么? // g3 退出后, context 将不再阻塞,g2 会随之退出 // g2 退出时,调用了 shutdown,g1 会退出 // 然后 main 函数中的 g.Wait() 退出,所有协程都会退出 g.Go(func() error {  quit := make(chan os.Signal, 0)  signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)   select {  case <-ctx.Done():   log.Println("g3, ctx execute cancel...")   log.Println("g3 error,", ctx.Err().Error())   // 当g2退出时,已经有错误了,此时的error 并不会覆盖到g中   return ctx.Err()  case sig := <-quit:   return fmt.Errorf("g3 get os signal: %v", sig)  } })  // g.Wait 等待所有 go执行完毕后执行 fmt.Printf("end, errgroup exiting, %+v\n", g.Wait())}

    运行测试

    D:\gopath\src\Go_base\daily_test\errorGroup>go run demo.go

    浏览器输入http://127.0.0.1:8099/shutdown

    控制台输出

    2021/12/11 10:52:03 g2, request `/shutdown`, server will out...
    2021/12/11 10:52:03 g1 error,will exit. http: Server closed
    2021/12/11 10:52:03 g3, ctx execute cancel...
    2021/12/11 10:52:03 g3 error, context canceled // 间隔了3s
    2021/12/11 10:52:06 shutting down server...
    end, errgroup exiting, http: Server closed

    从执行结果可以看出,这种退出可以保证每个goroutine都能在完成正在执行的工作后退出

    在terminal 按ctrl + c

    输出
    2021/12/11 10:55:51 g2 errgroup exit...
    2021/12/11 10:55:51 g1 error,will exit. http: Server closed
    2021/12/11 10:55:51 shutting down server...
    end, errgroup exiting, g3 get os signal: interrupt

    "Go并发编程中sync/errGroup怎么使用"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

    消费 消费者 数据 错误 处理 三个 函数 方法 阻塞 编程 代码 逻辑 保证 工作 控制 输出 信号 内容 就是 数据处理 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技 蓝思科技飞鸽互联网 互联网与科技金融数据 小马过河互联网科技 北京禾田雨橡互联网科技 顺玮阁互联网科技 广州紫鲸互联网科技 互联网科技咨询 镁科技互联网资讯平台 央行互联网金融科技公司 南山科技园互联网公司有哪些 宁夏众和广鑫互联网科技有限公司 崇明区互联网教育科技售后服务 新兴科技互联网创新大赛颁奖典礼 合肥互联网科技公司公章图片 怎样加入科技互联网 深圳互联网科技的发展 有人说阿里系是互联网科技企业 互联网时代科技型公司的机会 亚光科技低轨卫星互联网 上海喔噻互联网科技有限公司 亿人帮互联网科技有限公司 善班互联网科技有限公司 伴你行互联网科技有限公司 哈尔滨屹滨互联网科技有限公司 互联网新科技动漫ppt素材 西安小鹰速贷互联网科技有限公司 富国互联网科技002692 保山互联网科技靠谱吗 互联网对科技影响
    0