返回顶部
首页 > 资讯 > 后端开发 > GO >Go并发编程中sync/errGroup的使用
  • 895
分享到

Go并发编程中sync/errGroup的使用

GOsync 2022-06-07 20:06:21 895人浏览 安东尼
摘要

目录一.序二.errGroup2.1 函数签名三.源码3.1 Group3.2 WaitContext3.3 Go3.4 Wait四. 案例五. 参考一.序 这一篇算是并发编

目录

一.序

二.errGroup

2.1 函数签名

三.源码

3.1 Group

3.2 WaitContext

3.3 Go

3.4 Wait

四. 案例

五. 参考

一.序

这一篇算是并发编程的一个补充,起因是当前有个项目,大概の 需求是,根据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

分析都在代码注释中

五. 参考

https://lailin.xyz/post/go-training-week3-errgroup.html
https://pkg.go.dev/golang.org/x/sync/errgroup

到此这篇关于Go并发编程中sync/errGroup的使用的文章就介绍到这了,更多相关Go sync/errGroup内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!


您可能感兴趣的文档:

--结束END--

本文标题: Go并发编程中sync/errGroup的使用

本文链接: https://lsjlt.com/news/30721.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Go并发编程中sync/errGroup的使用
    目录一.序二.errGroup2.1 函数签名三.源码3.1 Group3.2 WaitContext3.3 Go3.4 Wait四. 案例五. 参考一.序 这一篇算是并发编...
    99+
    2022-06-07
    GO sync
  • Go并发编程中sync/errGroup怎么使用
    本篇内容介绍了“Go并发编程中sync/errGroup怎么使用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一.序这一篇算是并发编程的一个...
    99+
    2023-06-25
  • Go并发编程中使用channel的方法
    目录一.设计原理二.数据结构三.创建管道四. 发送数据4.1 直接发送4.2 缓冲区4.3 阻塞发送4.4 小结五. 接收数据5.1 直接接收5.2 缓冲区5.3 阻塞接收六. 关闭...
    99+
    2024-04-02
  • Go 中的并发编程:如何使用 PATH 打包并发?
    Go 语言是一门支持并发编程的语言,具有高效和简洁的特性。PATH 是 Go 中的一种并发编程模型,它可以帮助程序员在并发编程过程中更加方便地管理和控制协程的执行。在本文中,我们将探讨如何使用 PATH 打包并发,以及如何编写高效的并发代码...
    99+
    2023-10-01
    path 打包 并发
  • Go并发编程sync.Cond的具体使用
    目录简介详细介绍案例:Redis连接池注意点简介 Go 标准库提供 Cond 原语的目的是,为等待 / 通知场景下的并发问题提供支持。Cond 通常应用于等待某个条件的一组 g...
    99+
    2022-06-07
    GO sync
  • Go并发编程sync.Cond怎么使用
    本篇内容主要讲解“Go并发编程sync.Cond怎么使用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Go并发编程sync.Cond怎么使用”吧!简介Go 标准库提供 Cond 原语的目的是,为...
    99+
    2023-06-30
  • Go语言的并发编程和协程使用
    Go语言作为一种强大的编程语言,以其简洁、高效的特性而著称。其中,其强大的并发编程和协程使用是其最大的亮点之一。本文将介绍Go语言中并发编程和协程的原理以及具体使用方法,并提供一些代码...
    99+
    2024-03-02
    go语言 协程 并发
  • GO语言并发之好用的sync包详解
    目录sync.Map 并发安全的Mapsync.Once 只执行一次sync.Cond 条件变量控制小结sync.Map 并发安全的Map 反例如下,两个Goroutine分别读写。...
    99+
    2022-12-29
    GO语言 并发 sync包 GO语言 sync包 GO sync包
  • Go语言并发编程中的锁应用
    Go语言是一种开源编程语言,最初由Google开发,旨在提升程序员的效率和系统的性能。Go语言支持并发编程,也就是同时执行多个任务,其中使用锁是一种常见的方式来保证并发安全。在本文中,...
    99+
    2024-04-02
  • Go中同时使用并发和并行编程的案例分析
    go 同时支持并发 (通过 goroutine) 和并行 (通过协程) 以提升效率。goroutine 允许同时执行独立任务,而通道则实现 goroutine 之间的通信。该示例演示了如...
    99+
    2024-05-12
    并发 并行 质数
  • 使用Go语言开发高效的并发编程应用
    使用Go语言开发高效的并发编程应用随着互联网的快速发展和计算机性能的不断提升,人们对于软件系统的要求也越来越高。尤其是在网络应用开发中,高并发处理成为了一项重要的技术挑战。而Go语言作为一门强调并发编程的语言,逐渐成为了开发高效并发应用的首...
    99+
    2023-11-20
    Go语言 并发编程 高效应用
  • Go编程中并发和并行的区别及应用
    并发和并行是计算机领域中常见的概念,在Go编程中也有着重要的应用。本文将介绍并发和并行的区别,并结合具体的Go代码示例来说明它们在实际编程中的应用。 一、并发和并行的区别 在讨论并发和...
    99+
    2024-03-13
    go语言
  • Go 编程:如何使用 PATH 打包并发?
    在 Go 编程中,PATH 是一个非常重要的概念。它是用于设置环境变量的一个列表,其中包含了一系列路径,这些路径指示了操作系统在哪里查找可执行文件。在本篇文章中,我们将深入探讨如何使用 PATH 打包并发。 在 Go 中,我们可以使用 P...
    99+
    2023-10-01
    path 打包 并发
  • GO并发编程使用方法是什么
    这篇文章主要介绍了GO并发编程使用方法是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇GO并发编程使用方法是什么文章都会有所收获,下面我们一起来看看吧。啥是并发编程呢指在一台处理器上同时处理多个任务此处说的...
    99+
    2023-07-05
  • Go并发编程:资源管理与锁的使用
    go并发编程中资源管理和锁的使用至关重要。go提供了并发安全类型、通道和waitgroup来管理共享资源访问,而互斥锁、读写锁和原子操作则用于控制对资源的访问。实战案例展示了如何使用sy...
    99+
    2024-05-11
    并发编程 并发访问 同步机制 标准库
  • golang基于errgroup实现并发调用的方法
    目录串行调用基于sync.WaitGroup实现简单的并发调用基于errgroup.Group实现并发调用总结串行调用 在用go编写web/rpc服务器的时候,经常会出现需要对下游多...
    99+
    2024-04-02
  • 高效并发编程:使用Go WaitGroup和协程池
    在Go语言中,可以使用WaitGroup和协程池来实现高效的并发编程。1. WaitGroup:WaitGroup是一个计数器,用于...
    99+
    2023-10-08
    Golang
  • GO中sync包自由控制并发示例详解
    目录资源竞争sync.Mutexsync.RWMutexsync.WaitGroupsync.Oncesync.Cond资源竞争 channel 常用于并发通信,要保证并发安全,主要...
    99+
    2024-04-02
  • 高效并发编程实践:Golang中的Go WaitGroup使用技巧
    在Golang中,WaitGroup是一个并发原语,可以用于等待一组goroutine完成任务。下面是一些使用WaitGroup的技...
    99+
    2023-10-08
    Golang
  • Go中并发和并行编程的权衡利弊
    并发和并行编程在 go 中的权衡利弊:并发:适合低延迟响应,但不能充分利用多核,可能导致数据竞争。并行:充分利用多核,但开销较高,需做好共享状态同步。 Go 中并发和并行编程的权衡利弊...
    99+
    2024-05-12
    并发 并行
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作