Golang中WaitGroup使用的一点坑
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")
}
|
Golang中WaitGroup使用的一点坑相关推荐
- Golang中WaitGroup、Context、goroutine定时器及超时学习笔记
原文连接:http://targetliu.com/2017/5/2... 好久没有发过文章了 - -||,今天发一篇 golang 中 goroutine 相关的学习笔记吧,以示例为主. WaitG ...
- golang中的sync.WaitGroup
golang中的sync.WaitGroup Posted on 2015/04/09刚才看golang的sync的包,看见一个很有用的功能.就是WaitGroup. 先说说WaitGroup的用途: ...
- golang中创建logger时候踩过的坑
golang中创建logger时候踩过的坑 错误的代码 package mainimport ("fmt""io""log""os ...
- golang中container/list包中的坑
转载地址:golang中container/list包中的坑 - Go语言中文网 - Golang中文社区 golang中list包用法可以参看golang中container/list包用法_che ...
- Go实战--golang中defer的使用
原址 生命不止,继续 go go go !!! 学习golang这么久了,还没看到类似传统的 try-catch-finally 这种异常捕捉方式. 但是,Go中引入的Exception处理:def ...
- golang中并发sync和channel
golang中并发sync和channel chenbaoke · 2014-12-08 13:00:01 · 19151 次点击 · 预计阅读时间 5 分钟 · 不到1分钟之前 开始浏览 这是一个创 ...
- Golang中的自动伸缩和自防御设计
Raygun服务由许多活动组件构成,每个组件用于特定的任务.其中一个模块是用Golang编写的,负责对iOS崩溃报告进行处理.简而言之,它接受本机iOS崩溃报告,查找相关的dSYM文件,并生成开发者可 ...
- php协程和goroutine,golang中四种方式实现子goroutine与主协程的同步
如何实现子goroutine与主线程的同步 第一种方式:time.sleep(),这种方式很太死板,就不演示了. 第二种方式:使用channel机制,每个goroutine传一个channel进去然后 ...
- go语言os.exit(1)_在Golang中各种永远阻塞的姿势
在Golang中各种永远阻塞的姿势 Go的运行时的当前设计,假定程序员自己负责检测何时终止一个goroutine以及何时终止该程序. 可以通过调用os.Exit或从main()函数的返回来以正常方式终 ...
最新文章
- Intellij IDEA 中无法下载 Cloud Toolkit 问题解决
- 怎样将两个html页面合并,如何把WORD的两个页面合并在一起?
- js中的trim()方法
- c++99乘法表_nine-nine table 不是“99张桌子”,理解错了数学老师会打你!
- python显示no matching distribution,Python使用pip安装No matching distribution found for PyYaml==5.3.1...
- 国防科技大学计算机学院教授陈书民,国防科技大学刘新旺教授做客信息学院
- 走美团特色的无人配送道路,王慧文:久久为功才能做成
- 计算机考试题目czt,8级计算机组成原理本科期末试题a带答案(史岚).docx
- 2021-11-17
- 录音喊话器的故障修理_小鸭圣吉奥滚筒进水后不洗故障维修
- Nginx从入门到精通
- 百度网盘里的html怎么用,百度网盘怎么用?
- 弘兵金融学院 站在山顶 看不见山
- 微信小程序,小游戏数据助手数据爬取!还敢随便授权小游戏吗?
- java多线程之线程安全----铁路售票系统的实现
- 配置管理和变更管理_想要改善变更管理,消除对它的需要
- lanker php 大码,GitHub - TREYWANGCQU/LANKERS: CQU-LANKERS
- 金融危机的影响(ISAS课题)
- 1998世界杯主题曲[生命之杯]Ricky.Martin
- js对json对象的遍历和其他方法处理
热门文章
- 电脑如何测网速_职场人必备?告别加班的软件,100%提升工作效率|电脑|程序员|mac...
- html5实验原理,HTML5 方块碎片化实验
- java和seo学那个_Java和PHP编程语言哪个比较厉害?
- python怎么显示提示_Python中如何显示程序进度
- 大话数据结构 -04-3 队列
- OpenStack Cinder发展动态系列--Austin峰会
- linux编程之信号
- 修改Visual Studio中“添加新项”时默认添加的命名空间
- js代码格式化工具(格式化、压缩、加密压缩)
- 数据库原理—常用的DBS产品简介(六)