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使用的一点坑相关推荐

  1. Golang中WaitGroup、Context、goroutine定时器及超时学习笔记

    原文连接:http://targetliu.com/2017/5/2... 好久没有发过文章了 - -||,今天发一篇 golang 中 goroutine 相关的学习笔记吧,以示例为主. WaitG ...

  2. golang中的sync.WaitGroup

    golang中的sync.WaitGroup Posted on 2015/04/09刚才看golang的sync的包,看见一个很有用的功能.就是WaitGroup. 先说说WaitGroup的用途: ...

  3. golang中创建logger时候踩过的坑

    golang中创建logger时候踩过的坑 错误的代码 package mainimport ("fmt""io""log""os ...

  4. golang中container/list包中的坑

    转载地址:golang中container/list包中的坑 - Go语言中文网 - Golang中文社区 golang中list包用法可以参看golang中container/list包用法_che ...

  5. Go实战--golang中defer的使用

    原址 生命不止,继续 go go go !!! 学习golang这么久了,还没看到类似传统的 try-catch-finally 这种异常捕捉方式.  但是,Go中引入的Exception处理:def ...

  6. golang中并发sync和channel

    golang中并发sync和channel chenbaoke · 2014-12-08 13:00:01 · 19151 次点击 · 预计阅读时间 5 分钟 · 不到1分钟之前 开始浏览 这是一个创 ...

  7. Golang中的自动伸缩和自防御设计

    Raygun服务由许多活动组件构成,每个组件用于特定的任务.其中一个模块是用Golang编写的,负责对iOS崩溃报告进行处理.简而言之,它接受本机iOS崩溃报告,查找相关的dSYM文件,并生成开发者可 ...

  8. php协程和goroutine,golang中四种方式实现子goroutine与主协程的同步

    如何实现子goroutine与主线程的同步 第一种方式:time.sleep(),这种方式很太死板,就不演示了. 第二种方式:使用channel机制,每个goroutine传一个channel进去然后 ...

  9. go语言os.exit(1)_在Golang中各种永远阻塞的姿势

    在Golang中各种永远阻塞的姿势 Go的运行时的当前设计,假定程序员自己负责检测何时终止一个goroutine以及何时终止该程序. 可以通过调用os.Exit或从main()函数的返回来以正常方式终 ...

最新文章

  1. Intellij IDEA 中无法下载 Cloud Toolkit 问题解决
  2. 怎样将两个html页面合并,如何把WORD的两个页面合并在一起?
  3. js中的trim()方法
  4. c++99乘法表_nine-nine table 不是“99张桌子”,理解错了数学老师会打你!
  5. python显示no matching distribution,Python使用pip安装No matching distribution found for PyYaml==5.3.1...
  6. 国防科技大学计算机学院教授陈书民,国防科技大学刘新旺教授做客信息学院
  7. 走美团特色的无人配送道路,王慧文:久久为功才能做成
  8. 计算机考试题目czt,8级计算机组成原理本科期末试题a带答案(史岚).docx
  9. 2021-11-17
  10. 录音喊话器的故障修理_小鸭圣吉奥滚筒进水后不洗故障维修
  11. Nginx从入门到精通
  12. 百度网盘里的html怎么用,百度网盘怎么用?
  13. 弘兵金融学院 站在山顶 看不见山
  14. 微信小程序,小游戏数据助手数据爬取!还敢随便授权小游戏吗?
  15. java多线程之线程安全----铁路售票系统的实现
  16. 配置管理和变更管理_想要改善变更管理,消除对它的需要
  17. lanker php 大码,GitHub - TREYWANGCQU/LANKERS: CQU-LANKERS
  18. 金融危机的影响(ISAS课题)
  19. 1998世界杯主题曲[生命之杯]Ricky.Martin
  20. js对json对象的遍历和其他方法处理

热门文章

  1. 电脑如何测网速_职场人必备?告别加班的软件,100%提升工作效率|电脑|程序员|mac...
  2. html5实验原理,HTML5 方块碎片化实验
  3. java和seo学那个_Java和PHP编程语言哪个比较厉害?
  4. python怎么显示提示_Python中如何显示程序进度
  5. 大话数据结构 -04-3 队列
  6. OpenStack Cinder发展动态系列--Austin峰会
  7. linux编程之信号
  8. 修改Visual Studio中“添加新项”时默认添加的命名空间
  9. js代码格式化工具(格式化、压缩、加密压缩)
  10. 数据库原理—常用的DBS产品简介(六)