美文网首页
Go - sync.WaitGroup 协同等待,任务编排利器

Go - sync.WaitGroup 协同等待,任务编排利器

作者: kyo1992 | 来源:发表于2021-05-16 09:35 被阅读0次

    设计目的

    可以等待一组 Goroutine 的返回,一个比较常见的使用场景是批量发出 RPC 或者 HTTP 请求
    将原本顺序执行的代码在多个 Goroutine 中并发执行,加快程序处理的速度.

    WaitGroup基本用法

    提供三个方法

        func (wg *WaitGroup) Add(delta int)
        func (wg *WaitGroup) Done()
        func (wg *WaitGroup) Wait()
    
    • Add,用来设置 WaitGroup 的计数值;
    • Done,用来将 WaitGroup 的计数值减 1,其实就是调用了 Add(-1);
    • Wait,调用这个方法的 goroutine 会一直阻塞,直到 WaitGroup 的计数值变为 0。

    WaitGroup 的实现

    
    type WaitGroup struct {
        // 避免复制使用的一个技巧,可以告诉vet工具违反了复制使用的规则
        noCopy noCopy
        // 64bit(8bytes)的值分成两段,高32bit是计数值,低32bit是waiter的计数
        // 另外32bit是用作信号量的
        // 因为64bit值的原子操作需要64bit对齐,但是32bit编译器不支持,所以数组中的元素在不同的架构中不一样,具体处理看下面的方法
        // 总之,会找到对齐的那64bit作为state,其余的32bit做信号量
        state1 [3]uint32
    }
    

    在 64 位环境下,state1 的第一个元素是 waiter 数,第二个元素是 WaitGroup 的计数值,第三个元素是信号量。


    使用 WaitGroup 时的常见错误

    常见问题一:计数器设置为负值

    WaitGroup 的计数器的值必须大于等于 0。我们在更改这个计数值的时候,WaitGroup 会先做检查,如果计数值被设置为负数,就会导致 panic。

    常见问题二:不期望的 Add 时机

    在使用 WaitGroup 的时候,一定要遵循的原则就是,等所有的 Add 方法调用之后再调用 Wait,否则就可能导致 panic 或者不期望的结果。

    常见问题三:前一个 Wait 还没结束就重用 WaitGroup

    WaitGroup 虽然可以重用,但是是有一个前提的,那就是必须等到上一轮的 Wait 完成之后,才能重用 WaitGroup 执行下一轮的 Add/Wait,如果你在 Wait 还没执行完的时候就调用下一轮 Add 方法,就有可能出现 panic。

    总结

    避免错误使用 WaitGroup 的情况,只需要尽量保证下面 5 点就可以了:

    • 不重用 WaitGroup。新建一个 WaitGroup 不会带来多大的资源开销,重用反而更容易出错。
    • 保证所有的 Add 方法调用都在 Wait 之前。
    • 不传递负数给 Add 方法,只通过 Done 来给计数值减 1。
    • 不做多余的 Done 方法调用,保证 Add 的计数值和 Done 方法调用的数量是一样的。
    • 不遗漏 Done 方法的调用,否则会导致 Wait hang 住无法返回。

    相关文章

      网友评论

          本文标题:Go - sync.WaitGroup 协同等待,任务编排利器

          本文链接:https://www.haomeiwen.com/subject/bcxcjltx.html