golang sync.errgroup 源代码阅读

疯狂的小蚂蚁go · · 975 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

errgroup用途

调用多个go程做访问一批url,它们是有失败的可能,如何把第一个出错的信息返回给调用端。这涉及到多go程返回错误的技巧。你可以用一个错误chan chan error返回错误,调用端读取错误<- err,涉及到读取context.Done,只能使用select这个大杀器滥听两路以上chan,你需要痛苦地写一些没有营养的模板代码,你很希望有人能救你,这时就该到我们的sync.errgroup登场

一段代码(多go程中返回第一个错误)

使用很简单,声明,使用,等待

  • 声明 var g errgroup.Group
  • 使用,这里起go程使用errgroup的g.Go回调函数
  • 等待,if err := g.Wait(); err != nil {/*打印错误*/}
    var g errgroup.Group
    var urls = []string{
        "http://www.golang.org/",
        "http://www.google.com/",
        "http://www.somestupidname.com/",
    }
    for _, url := range urls {
        // Launch a goroutine to fetch the URL.
        url := url // https://golang.org/doc/faq#closures_and_goroutines
        g.Go(func() error {
            // Fetch the URL.
            resp, err := http.Get(url)
            if err == nil {
                resp.Body.Close()
            }
            return err
        })
    }
    // Wait for all HTTP fetches to complete.
    if err := g.Wait(); err == nil {
        fmt.Println("Successfully fetched all URLs.")
    }

一探errgroup数据结构

  • err error 保存第一个错误
  • errOnce sync.Once errOnce可以保证它的回调函数只执行一次,这正是我们需要的,只返回第一个错误
  • wg sync.WaitGroup 可以等待所有go程结束
  • cancel func() 这里保存的是contex.WithCancel返回的第二个参数。
    总结: 变量wg可以保证在所有go程结束,这时候返回err的值。在赋值的时候,只需要第一个值,这里用到errOnce变量保证(可以看下上篇的sync.Once源代码解析)。cancel有值的情况下,回调函数出错会被调用(context.Context现在有点chan里面软中断的意味)。
type Group struct {
    cancel func()

    wg sync.WaitGroup

    errOnce sync.Once
    err     error
}

WithContext接口

这接口主要是保存context.WithCancel返回的结果

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

Wait 接口

这里使用了g.wg.Wait函数等待所有go程结束,err有值,意味着出错,我们通过return告诉调用端。如果设置了cancel,我们通过调用cancel关闭这个context.Context

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

go接口

  • 首先,要保证g.wg.Wait调用正确,在go程起之前调用g.wg.Add(1)加上1个,结束之后通过defer g.wg.Done()减去1个。
  • 无错是相安无事,这里看下出错。g.errOnce.DO保证回调函数会被执行一次。只做一次的事,就是把第一个错误收集起来g.err = err, 如果设置了cancel也关闭了g.cancel()
func (g *Group) Go(f func() error) {
    g.wg.Add(1)

    go func() {
        defer g.wg.Done()

        if err := f(); err != nil {
            g.errOnce.Do(func() {
                g.err = err
                if g.cancel != nil {
                    g.cancel()
                }
            })
        }
    }()
}

有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:疯狂的小蚂蚁go

查看原文:golang sync.errgroup 源代码阅读

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

975 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传