编程沉思录

编程沉思录

马上订阅 编程沉思录 RSS 更新: https://www.cyhone.com/atom.xml

Golang WaitGroup 原理深度剖析

2021年1月17日 16:35

sync.WaitGroup 是 Golang 中常用的并发措施,我们可以用它来等待一批 Goroutine 结束。

WaitGroup 的源码也非常简短,抛去注释外也就 100 行左右的代码。但即使是这 100 行代码,里面也有着关乎内存优化、并发安全考虑等各种性能优化手段。

本文将基于 go-1.13 的源码 进行分析,将会涉及以下知识点:

  1. WaitGroup 的实现逻辑
  2. WaitGroup 的底层内存结构及性能优化
  3. WaitGroup 的内部如何实现无锁操作

WaitGroup 的使用

在正式分析源码之前,我们先看下 WaitGroup 的基本用法:

1
2
3
4
5
6
7
8
9
10
11
12
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go func() {
defer wg.Done()
println("hello")
}()
}

wg.Wait()
}

从上述代码可以看出,WaitGroup 的用法非常简单:使用 Add 添加需要等待的个数,使用 Done 来通知 WaitGroup 任务已完成,使用 Wait 来等待所有 goroutine 结束。

WaitGroup 的实现逻辑

我们首先看下 WaitGroup 的组成结构,代码如下:

1
2
3
4
type WaitGroup struct {
noCopy noCopy
state1 [3]uint32
}

其中 noCopy 是 golang 源码中检测禁止拷贝的技术。如果程序中有 WaitGroup 的赋值行为,使用 go vet 检查程序时,就会发现有报错。但需要注意的是,noCopy 不会影响程序正常的编译和运行。

state1 [3]uint32 字段中包含了 WaitGroup 的所有状态数据。该字段的整个设计其实非常复杂,为了便于快速理解 WaitGroup 的主流程,我们将在后面部分单独剖析 state1

为了便于理解 WaitGroup 的整个实现过程,我们暂时先不考虑内存对齐和并发安全等方面因素。那么 WaitGroup 可以近似的看做以下代码:

1
2
3
4
5
type WaitGroup struct {
counter int32
waiter uint32
sema uint32
}

其中:

  • counter 代表目前尚未完成的个数。WaitGroup.Add(n) 将会导致 counter +=...

剩余内容已隐藏

查看完整文章以阅读更多