Golang中WaitGroup使用的一点坑

Stella981
• 阅读 691

Golang中WaitGroup使用的一点坑

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

坑1

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

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")

}

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

1

2

3

2016/11/27 15:12:36 exit

[Finished in 0.7s]

或这样:

1

2

3

4

2016/11/27 15:21:51 i:2

2016/11/27 15:21:51 exit

[Finished in 0.8s]

或这样:

1

2

3

4

5

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 操作了。

于是代码改成了这样:

坑2

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

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 又睡了过去,而且是睡死了过去:

1

2

3

4

5

6

7

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 就死锁了。于是代码改成了这样:

填坑

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

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…

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
peter peter
3年前
Golang WaitGroup 原理深度剖析
sync.WaitGroup是Golang中常用的并发措施,我们可以用它来等待一批Goroutine结束。WaitGroup的源码也非常简短,抛去注释外也就100行左右的代码。但即使是这100行代码,里面也有着关乎内存优化、并发安全考虑等各种性能优化手段。本文将基于go1.13的源码进行分析,将会涉及以下知识点:1
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Stella981 Stella981
2年前
Golang WaitGroup源码分析
针对Golang1.9的sync.WaitGroup进行分析,与Golang1.10基本一样除了将panic改为了throw之外其他的都一样。源代码位置:sync\waitgroup.go。结构体typeWaitGroupstruct{noCopynoCopy//noCopy可以嵌入到结构中
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
为什么mysql不推荐使用雪花ID作为主键
作者:毛辰飞背景在mysql中设计表的时候,mysql官方推荐不要使用uuid或者不连续不重复的雪花id(long形且唯一),而是推荐连续自增的主键id,官方的推荐是auto_increment,那么为什么不建议采用uuid,使用uuid究
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这