-
参考文章
Go语言并发之道 -
sync包
sync包 包含对低级别内存访问同步最有用的并发原语 -
WaitGroup
当你不关心并发操作的结果,或者你有其它方法收集它们的结果时,waitgroup是
等待一组并发操作完成的好方法。 -
WaitGroup使用
Add(): 增加计数器的增量,会检测计数器数量,<0 会panic
Done(): 对计数器递减,等同于Add(-1)
Wait(): 阻塞,直到计数器为零var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() fmt.Println("1st goroutine sleeping...") time.Sleep(1 * time.Second) }() wg.Add(1) go func() { defer wg.Done() fmt.Println("2nd goroutine sleeping...") time.Sleep(1 * time.Second) }() wg.Wait() fmt.Println("All goroutine complete...")
-
注意事项
- deadlock
先看一段代码
var wg sync.WaitGroup wg.Add(1) go func(wg sync.WaitGroup) { defer wg.Done() fmt.Println("hello world") }(wg) wg.Wait()
运行之后会报错:
fatal error: all goroutines are asleep - deadlock!
和上面的示例代码不一样,这里不在是闭包环境,传入进去的wg是结构体的copy
值。需要传入结构体的指针go func(wg sync.WaitGroup) { defer wg.Done() fmt.Println("hello world") }(wg)
这样就没有问题了。
- negative WaitGroup counter
看一段代码
var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() fmt.Println("1st goroutine sleeping...") time.Sleep(1) }() go func() { defer wg.Done() fmt.Println("2nd goroutine sleeping...") time.Sleep(2) }() wg.Wait() fmt.Println("All goroutine complete...")
直接运行之后,会发现没有问题。
如果在最后加一行代码time.Sleep(1 * time.Second) // 停顿一秒
就会发现程序报错:
1st goroutine sleeping... 2nd goroutine sleeping... All goroutine complete... panic: sync: negative WaitGroup counter
从打印的结果分析,就能看出,两个goroutine都在最后执行了wg.Done(),而
计数器只有1。所以最后执行的Done()会报错。正确示例看本文第一段示例代码这里很多细节值得琢磨,留一个坑位后续再补充
- deadlock
Golang从入门到放弃200630--waitgroup
最新推荐文章于 2024-09-08 13:23:35 发布