sync.WaitGroup
介绍
当我们在开发过程中,经常需要在开启多个goroutine后,等待全部的goroutine执行完毕后才进行下一步的业务逻辑执行。此时我们可能会采用轮询的方式去定时侦测已经开启的多个goroutine的业务是否执行完毕,但是这样性能很低,并且持续占用cpu时间片很消耗cpu的资源,此时我们就该使用sync.WaitGroup
来完成此次操作。举个🌰,下列代码是开了10个goroutine后等待其各睡眠5秒之后进行后续操作的sync.WaitGroup
方法实现。
func main() {
// 创建对象
var wait sync.WaitGroup
for i := 0; i < 10; i++ {
// 为需要等待结束的goroutine数量+1
wait.Add(1)
go func() {
time.Sleep(5*time.Second)
// 结束 使得需要等待的数量-1
// 等同于 wait.Add(-1)
wait.Done()
}()
}
// 等待所有执行完毕
wait.Wait()
fmt.Println("wait done")
}
上述代码的睡眠5秒可以替换为任何需要在goroutine中执行的业务逻辑,上述代码中出现了下述几个sync.WaitGroup
中提供的方法,提供方法很少很简洁,接下来就开始解析一下sync.WaitGroup
的相关信息。
func (wg *WaitGroup) Add(delta int)
func (wg *WaitGroup) Done()
func (wg *WaitGroup) Wait()
sync.WaitGroup
源代码解析
1:sync.WaitGroup
结构体的解析
type WaitGroup struct {
// 一个防止sync.WaitGroup被复制的标记结构体
noCopy noCopy
// 该数组在32为系统与64位系统中代表的用途不同
// 首先说64位系统:
// state1[0]代表当前sync.WaitGroup 调用Add方法增加了多少的couter
// state1[1]代表调用了Wait方法等待结束的Waiter的数量
// state1[2]代表Waiter的信号量
// 其中 state1[0]与state1[1]作者称为计数标记
// 在32位系统中:
// state1[0]代表Waiter的信号量
// state1[1]代表当前sync.WaitGroup 调用Add方法增加了多少的couter
// state1[2]代表调用了Wait方法等待结束的Waiter的数量
// 其中 state1[1]与state1[2]作者称为计数标记
state1 [3]uint32
}
Add(delta int)
方法源代码解析
// state 方法用于根据系统是32位还是64位返回对应的state1字段的对应的计数和信号量的地址
func (wg *WaitGroup) state() (statep *uint64, semap *uint32) {
if uintptr(unsafe.Pointer(&wg.state1))%8 == 0 {
// 64位系统返回state1与state1[2],由于数组是连续内存所以可以通过首地址