Go语言WaitGroup使用时需要注意的坑
前言
WaitGroup在go语言中,用于线程同步,单从字面意思理解,wait等待的意思,group组、团队的意思,WaitGroup就是指等待一组,等待一个系列执行完成后才会继续向下执行。Golang中的WaitGroup一直是同步goroutine的推荐实践。自己用了两年多也没遇到过什么问题。
直到最近的一天同事扔过来一段奇怪的代码:
第一个坑
packagemain import( "log" "sync" ) funcmain(){ wg:=sync.WaitGroup{} fori:=0;i<5;i++{ gofunc(wgsync.WaitGroup,iint){ wg.Add(1) log.Printf("i:%d",i) wg.Done() }(wg,i) } wg.Wait() log.Println("exit") }
撇了一眼,觉得没什么问题。
然而,它的运行结果是这样:
2016/11/2715:12:36exit [Finishedin0.7s]
或这样:
2016/11/2715:21:51i:2 2016/11/2715:21:51exit [Finishedin0.8s]
或这样:
2016/11/2715:22:51i:3 2016/11/2715:22:51i:2 2016/11/2715:22:51exit [Finishedin0.8s]
一度让我以为手上的mac也没睡醒……
这个问题如果理解了WaitGroup的设计目的就非常容易fix啦。因为WaitGroup同步的是goroutine,而上面的代码却在goroutine中进行Add(1)操作。因此,可能在这些goroutine还没来得及Add(1)已经执行Wait操作了。
于是代码改成了这样:
第二个坑
packagemain import( "log" "sync" ) funcmain(){ wg:=sync.WaitGroup{} fori:=0;i<5;i++{ wg.Add(1) gofunc(wgsync.WaitGroup,iint){ log.Printf("i:%d",i) wg.Done() }(wg,i) } wg.Wait() log.Println("exit") }
然而,mac又睡了过去,而且是睡死了过去:
2016/11/2715:25:16i:1 2016/11/2715:25:16i:2 2016/11/2715:25:16i:4 2016/11/2715:25:16i:0 2016/11/2715:25:16i:3 fatalerror:allgoroutinesareasleep-deadlock!
wg给拷贝传递到了goroutine中,导致只有Add操作,其实Done操作是在wg的副本执行的。因此Wait就死锁了。
于是代码改成了这样:
填坑
packagemain import( "log" "sync" ) funcmain(){ wg:=&sync.WaitGroup{} fori:=0;i<5;i++{ wg.Add(1) gofunc(wg*sync.WaitGroup,iint){ log.Printf("i:%d",i) wg.Done() }(wg,i) } wg.Wait() log.Println("exit") }
总结
好了,到这里终于解决了,以上就是关于Go语言WaitGroup使用时需要注意的一些坑,希望本文中提到的这些问题对大家学习或者使用Go语言的时候能有所帮助,如果有疑问大家可以留言交流。