go WaitGroup的使用

WaitGroup用于等待一组线程的结束。父线程调用Add方法来设定应等待的线程的数量。每个被等待的线程在结束时应调用Done方法。同时,主线程里可以调用Wait方法阻塞至所有线程结束。 
但在使用时,也有一些问题需要注意,请看本文的详细分解。 
另外,WaitGroup的使用场景十分有限,为什么呢?具体原因,请看本文的总结部分的分析。

主要函数

func (*WaitGroup) Add

func (wg *WaitGroup) Add(delta int)

Add方法向内部计数加上delta,delta可以是负数;如果内部计数器变为0,Wait方法阻塞等待的所有协程都会释放,如果计数器小于0,则调用panic。注意Add加上正数的调用应在Wait之前,否则Wait可能只会等待很少的协程。一般来说本方法应在创建新的协程或者其他应等待的事件之前调用。

func (wg *WaitGroup) Done()

func (wg *WaitGroup) Done()

Done方法减少WaitGroup计数器的值,应在协程的最后执行。

func (wg *WaitGroup) Wait()

func (wg *WaitGroup) Wait() 

Wait方法阻塞直到WaitGroup计数器减为0。

编程实战

等待某个协程结束

func main() {
    var wg sync.WaitGroup

    wg.Add(1)
    go func() {
        defer wg.Done()
        fmt.Println("1 goroutine sleep ...")
        time.Sleep(2e9)
        fmt.Println("1 goroutine exit ...")
    }()

    wg.Add(1)
    go func() {
        defer wg.Done()
        fmt.Println("2 goroutine sleep ...")
        time.Sleep(4e9)
        fmt.Println("2 goroutine exit ...")
    }()

    fmt.Println("waiting for all goroutine ")
    wg.Wait()
    fmt.Println("All goroutines finished!")
}

小结

要注意Add和Done函数一定要配对,否则可能发生死锁,所报的错误信息如下:

fatal error: all goroutines are asleep - deadlock!
等待协程组结束

func main() {
    sayHello := func(wg *sync.WaitGroup, id int) {
        defer wg.Done()
        fmt.Printf("%v goroutine start ...\n", id)
        time.Sleep(2)
        fmt.Printf("%v goroutine exit ...\n", id)
    }

    var wg sync.WaitGroup
    const N = 5
    wg.Add(N)
    for i := 0; i < N; i++ {
        go sayHello(&wg, i)
    }

    fmt.Println("waiting for all goroutine ")
    wg.Wait()
    fmt.Println("All goroutines finished!")
}

运行以上程序,输出如下:

waiting for all goroutine 
1 goroutine start ...
5 goroutine start ...
5 goroutine exit ...
4 goroutine start ...
4 goroutine exit ...
3 goroutine start ...
1 goroutine exit ...
0 goroutine start ...
3 goroutine exit ...
0 goroutine exit ...
2 goroutine start ...
2 goroutine exit ...
All goroutines finished!

无论运行多少次,都能保证All goroutines finished!这一句在最后一行输出,这说明,Wait()函数等了所有协程都结束自己才返回。

小结

以上程序通过WaitGroup提供的三个同步接口,实现了等待一个协程组完成的同步操作。在实现时要注意: 
* Add的参数N必须和创建的goroutine的数量相等,否则会报出死锁的错误信息 
* 另外,sayHello()函数中要传递WaitGroup的指针呢?在该结构的实现源码中已经有讲解:

A WaitGroup must not be copied after first use.
就是说,该结构定义后就不能被复制,所以这里要使用指针。
总结

通过WaitGroup提供的三个函数:Add,Done,Wait,可以轻松实现等待某个协程或协程组完成的同步操作。但在使用时要注意:

Add的数量和Done的调用数量必须相等。
另外,就是WaitGroup结构一旦定义就不能复制的原因。
WaitGroup在需要等待多个任务结束再返回的业务来说还是很有用的,但现实中用的更多的可能是,先等待一个协程组,若所有协程组都正确完成,则一直等到所有协程组结束;若其中有一个协程发生错误,则告诉协程组的其他协程,全部停止运行(本次任务失败)以免浪费系统资源。 
该场景WaitGroup是无法实现的,那么该场景该如何实现呢,就需要用到通知机制,其实也可以用channel来实现,具体的解决办法,请看后续的文章。 
这样说来,WaitGroup的使用场景是有限的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值