使用 Golang 的同学肯定对 WaitGroup 并不陌生,它的使用符合我们常见的思维方式并且使用也很简单,但是在简单之下也暗藏一下陷阱,今天就来看一下一个比较隐蔽的陷阱。
错误示例
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
wg := sync.WaitGroup{}
var v uint64
for i := 0; i < 3; i++ {
go func() {
wg.Add(1)
atomic.AddUint64(&v, 1)
wg.Done()
}()
}
wg.Wait()
fmt.Println(v)
}
首先看一下上面这个示例,代码很简单开启协程去原子累加,但是它的结果会如我们所料的是3吗,答案不是,不信你可以去执行一下代码,也许是0也许是1,那么为什么会这样呢?问题的核心是我们执行 wg.Add 的时机不对,我们是在开启的协程里面来执行的,那么可能就会存在这样的情况,在 main groutine 执行 Wait 操作的时候,wg 的值已经是0了,所以整个的执行次数就不会和我们设想的一样
正确示例
package main
import (
"fmt"
"sync"
"sync/atomic"
)
func main() {
wg := sync.WaitGroup{}
var v uint64
for i := 0; i < 3; i++ {
wg.Add(1)
go func() {
atomic.AddUint64(&v, 1)
wg.Done()
}()
}
wg.Wait()
fmt.Println(v)
}
那么该怎样避免这种情况呢?就是我们的 Add 操作要在进入协程之前来进行就可以,所以可以在循环外层执行,也可以在循环内进入协程之前执行