引言 上篇我们学习了golang同步原语中的mutex,也介绍了为什么要使用同步原语以及mutex的底层结构和使用中注意事项。那么本篇我们继续来学习sync中同步原语中的waitgroup
感兴趣的可以阅读mutex初探
sync.WaitGroup初识
定义:WaitGroup的作用是等待其他进程结束后,在进行其他的操作。我们可以利用channle来实现多个协程之间同步,但是sync.waitGroup属于开箱即用,而且性能来说会更好些。
首先我们先看下面的代码
翠花上代码:
// 定义一个main函数共享变量
shared := 0
// 定义四个goroutine 分别对shared进行+,-,x,/
go func() {
shared += 1
fmt.Printf("this result of shared +1 = [%v] \n", shared)
}()
go func() {
shared = shared * 2
fmt.Printf("this result of shared *2 = [%v] \n", shared)
}()
go func() {
shared = shared - 2
fmt.Printf("this result of shared -2 = [%v] \n", shared)
}()
go func() {
shared = shared / 2
fmt.Printf("this result of shared/2 = [%v] \n", shared)
}()
time.Sleep(5 * time.Second)
翠花上图片
图1:
WaitGroup的使用
在mutex的使用上来说我们会在主进程中设置一个睡眠时间来等待所有进程的结束,但是如果使用WaitGroup就能够等待所有协程完成任务之后在执行主协程了。
// 初始化WaitGroup
wg := sync.WaitGroup{}
// 定义一个main函数共享变量
shared := 0
// 定义四个goroutine 分别对shared进行+,-,x,/
wg.Add(1)
go func() {
shared += 1
fmt.Printf("this result of shared +1 = [%v] \n", shared)
wg.Done()
}()
wg.Add(1)
go func() {
shared = shared * 2
fmt.Printf("this result of shared *2 = [%v] \n", shared)
wg.Done()
}()
wg.Add(1)
go func() {
shared = shared - 2
fmt.Printf("this result of shared -2 = [%v] \n", shared)
wg.Done()
}()
wg.Add(1)
go func() {
shared = shared / 2
fmt.Printf("this result of shared/2 = [%v] \n", shared)
wg.Done()
}()
wg.Wait()
翠花上图片
从代码中我们可以看到使用WaitGroup的方式很简单,仅仅四步就可以使四个协程同步完成。
- 初始化WaitGroup且只能被初始一次
- 对协程进行计数即调用add方法对等待同步的协程+1
- 协程执行完毕计数-1
- 在等待协程中调用wait方法,等待其余协程执行完毕
分析
分析WaitGroup的数据结构
type WaitGroup struct {
noCopy noCopy
state1 [3]uint32
}
noCopy: 简单来说noCopy保证了 WaitGroup用来保证不会被在赋值拷贝
state1:存储了状态和信号量
Add,done,wait方法
Add(delta int):主要是更新counter计数器即用来告诉程序对等待同步的协程+1,理论上来说 delta可以传-1,但是在没有等待的同步的协程中传入-1 程序会发生painc,
panic: sync: negative WaitGroup counter
Done(): 此方法主要是向Add方法中传入-1,本质上只是对Add进行了封装。不在赘述
// Done decrements the WaitGroup counter by one.
func (wg *WaitGroup) Done() {
wg.Add(-1)
}
Wait():wait方法是等待计数器counter为0时,当 sync.WaitGroup 的计数器归零时,陷入睡眠状态的 Goroutine 会被唤醒,上述方法也会立刻返回。
总结:
1.sync.WaitGroup 只有在wait方法结束后才能再次调用
2.Done()只是对Add方法的简单封装。
- Add(daleta)可以传入任意参数,但是传入负数时,必须保证当前计数不小于传入的负数和不小于0,否则会发生panic。
最后
关于sync.WaitGroup的使用介绍到这里了,如果有兴趣可以关注老王的个人公众号:
宇宇独行