12

Golang中WaitGroup使用的一点坑

 3 years ago
source link: https://liudanking.com/golang/golang-waitgroup-usage/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Golang中WaitGroup使用的一点坑

发表于2016-11-27

Golang中WaitGroup使用的一点坑

Golang 中的 WaitGroup 一直是同步 goroutine 的推荐实践。自己用了两年多也没遇到过什么问题。直到一天午睡后,同事扔过来一段奇怪的代码:

package main
import (
    "log"
    "sync"
func main() {
    wg := sync.WaitGroup{}
    for i := 0; i < 5; i++ {
        go func(wg sync.WaitGroup, i int) {
            wg.Add(1)
            log.Printf("i:%d", i)
            wg.Done()
        }(wg, i)
    wg.Wait()
    log.Println("exit")

撇了一眼,觉得没什么问题。然而,它的运行结果是这样:

2016/11/27 15:12:36 exit
[Finished in 0.7s]
2016/11/27 15:21:51 i:2
2016/11/27 15:21:51 exit
[Finished in 0.8s]
2016/11/27 15:22:51 i:3
2016/11/27 15:22:51 i:2
2016/11/27 15:22:51 exit
[Finished in 0.8s]

一度让我以为手上的 mac 也没睡醒……
这个问题如果理解了 WaitGroup 的设计目的就非常容易 fix 啦。因为 WaitGroup 同步的是 goroutine, 而上面的代码却在 goroutine 中进行 Add(1) 操作。因此,可能在这些 goroutine 还没来得及 Add(1) 已经执行 Wait 操作了。

于是代码改成了这样:

package main
import (
    "log"
    "sync"
func main() {
    wg := sync.WaitGroup{}
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(wg sync.WaitGroup, i int) {
            log.Printf("i:%d", i)
            wg.Done()
        }(wg, i)
    wg.Wait()
    log.Println("exit")

然而,mac 又睡了过去,而且是睡死了过去:

2016/11/27 15:25:16 i:1
2016/11/27 15:25:16 i:2
2016/11/27 15:25:16 i:4
2016/11/27 15:25:16 i:0
2016/11/27 15:25:16 i:3
fatal error: all goroutines are asleep - deadlock!

wg 给拷贝传递到了 goroutine 中,导致只有 Add 操作,其实 Done操作是在 wg 的副本执行的。因此 Wait 就死锁了。于是代码改成了这样:

package main
import (
    "log"
    "sync"
func main() {
    wg := &sync.WaitGroup{}
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(wg *sync.WaitGroup, i int) {
            log.Printf("i:%d", i)
            wg.Done()
        }(wg, i)
    wg.Wait()
    log.Println("exit")

至此,午睡终于睡醒了。Sigh…

–EOF–

版权声明
转载请注明出处,本文原始链接

此条目由daniel发表在Golang分类目录,并贴了golanggoroutinesynchronizeWaitGroup标签。将固定链接加入收藏夹。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK