1.简介

sync.WaitGroup 用于阻塞等待一组 Go 程的结束。主 Go 程调用 Add() 来设置等待的 Go 程数,然后该组中的每个 Go 程都需要在运行结束时调用 Done(), 递减 WaitGroup 的 Go 程计数器 counter。当 counter 变为 0 时,主 Go 程被唤醒继续执行。

type WaitGroup struct {// contains filtered or unexported fields
}// 设置需要等待的 Go 程数量
func (wg *WaitGroup) Add(delta int)// Go 程计数器减 1
func (wg *WaitGroup) Done()// 阻塞等待所有 Go 程结束(等待 Go 程计数器变为 0)
func (wg *WaitGroup) Wait()

WaitGroup 有三个方法,其中 Done() 调用了 Add(-1)。标准用法:
(1)启动 Go 程时调用 Add();
(2)在 Go 程结束时调用 Done();
(3)最后调用 Wait()。

2.使用示例

package mainimport ("fmt""sync"    "time"
)func foo1() {defer wg.Done()fmt.Println("exit foo1")
}func foo2() {defer wg.Done()fmt.Println("exit foo2")
}func main() {fmt.Println("entry main")var wg sync.WaitGroupwg.Add(2)go foo1()go foo2()fmt.Println("wg.Wait()")wg.Wait()fmt.Println("exit main")
}

编译运行输出:

entry main
wg.Wait()
exit foo2
exit foo1
exit main

注意: 多次执行输出结果中 “exit foo2” 和 “exit foo1” 的先后顺序是不定的,因为多协程并发执行的顺序是随机的。

3.错误示例

如果使用过程中通过 Add() 添加的 Go 程数与调用 Done() 的次数不符,即 sync.WaitGroup 的 Go 程计数器等所有子 Go 程结束后不为 0,则会引发 panic。

3.1 Done() 过多

func main() {fmt.Println("entry main")var wg sync.WaitGroupwg.Done()fmt.Println("wg.Wait()")wg.Wait()fmt.Println("exit main")
}

编译运行输出:

entry main
panic: sync: negative WaitGroup countergoroutine 1 [running]:
sync.(*WaitGroup).Add(0xc4200140d0, 0xffffffffffffffff)/usr/lib/golang/src/sync/waitgroup.go:75 +0x134
sync.(*WaitGroup).Done(0xc4200140d0)/usr/lib/golang/src/sync/waitgroup.go:100 +0x34
main.main()/data/goTest/src/waitgroup/main.go:34 +0x8e

可见,当 Go 程计数器变为负数时,将引发 panic。

3.2 Done() 过少

注释掉 foo2() 中的 defer wg.Done(),使得 Go 程结束时不减少 sync.WaitGroup 的 Go 程计数器。

func foo2() {//defer wg.Done()fmt.Println("exit foo2")
}

编译运行输出:

entry main
wg.Wait()
exit foo1
exit foo2
fatal error: all goroutines are asleep - deadlock!goroutine 1 [semacquire]:
sync.runtime_Semacquire(0x54aa7c)/usr/lib/golang/src/runtime/sema.go:56 +0x39
sync.(*WaitGroup).Wait(0x54aa70)/usr/lib/golang/src/sync/waitgroup.go:131 +0x72
main.main()/data/goTest/src/waitgroup/main.go:33 +0x10e

这个错误表明,在最后一个活动线程 foo2 退出的时候,Go 检测到当前没有还在运行的 Go 程,但主 Go 程仍在等待,发生了死锁现象,于是引发 panic,这是 Go 的一种自我保护机制。

4.errgroup

鉴于使用 sync.WaitGroup 容易出错,Go 官方包 errgroup 进一步对 sync.WaitGroup 进行了封装,不再需要 Add 和 Done,用起来更加简单方便。详见 golang.org/x/sync/errgroup。

package mainimport ("fmt""sync""golang.org/x/sync/errgroup"
)func foo1() {fmt.Println("exit foo1")
}func foo2() {fmt.Println("exit foo2")
}func main() {fmt.Println("entry main")var g errgroup.Groupg.Go(func() error {foo1()return nil})g.Go(func() error {foo2()return nil})fmt.Println("g.Wait()")g.Wait()fmt.Println("exit main")
}

运行输出:

entry main
wg.Wait()
exit foo2
exit foo1
exit main

参考文献

[1] Golang
[2] golang语言异步通信之WaitGroup

Golang sync.WaitGroup 简介与用法相关推荐

  1. Golang sync.Pool 简介与用法

    文章目录 1.简介 2.应用 2.1 标准库的应用 2.2 复用 bytes.Buffer 参考文章 1.简介 Pool 是可伸缩.并发安全的临时对象池,用来存放已经分配但暂时不用的临时对象,通过对象 ...

  2. Golang sync.Cond 简介与用法

    文章目录 1.简介 2.使用场景 3.原理 4.使用示例 5.注意事项 参考文章 1.简介 sync.Cond 是基于互斥锁/读写锁实现的条件变量,用来协调想要访问共享资源的那些 Goroutine. ...

  3. Golang sync.Map 简介与用法

    Golang 中的 map 在并发情况下,只读是线程安全的,并发读写线程不安全.为了解决这个问题,Golang 提供了语言层级的并发读写安全的 sync.Map. type Map struct {/ ...

  4. Golang sync.Once 简介与用法

    文章目录 1.简介 2.延迟初始化 参考文献 1.简介 sync.Once用来保证函数只执行一次.要达到这个效果,需要做到两点: (1)计数器,统计函数执行次数: (2)线程安全,保障在多 Go 程的 ...

  5. golang goroutine 协程同步 sync.WaitGroup 简介

    介绍 经常会看到以下了代码: package mainimport ("fmt""time" )func main(){for i := 0; i < 1 ...

  6. golang sync WaitGroup

    2019独角兽企业重金招聘Python工程师标准>>> 简介 它能够一直等到所有的goroutine执行完成,并且阻塞主线程的执行,直到所有的goroutine执行完成. WaitG ...

  7. Go sync.WaitGroup的用法

    介绍 经常会看到以下了代码: package mainimport ("fmt""time" )func main(){for i := 0; i < 1 ...

  8. golang中的sync.WaitGroup

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

  9. Golang gorouting 并发控制 sync.WaitGroup 介绍与使用

    WaitGroup简称同步组,用于等待goroutines结束的. 官方文档: type WaitGroupA WaitGroup waits for a collection of goroutin ...

最新文章

  1. 认识计算机ppt课件游戏,认识计算机)ppt课件
  2. 01_字符串处理-----04_在文本中应用ZIpf定律
  3. 深入理解Java 8 Lambda(语言篇——lambda,方法引用,目标类型和默认方法)
  4. Spark在集群中的安装
  5. 基于认证的代理平台搭建配置squid-20130730
  6. Java中ArrayList最大容量为什么是Integer.MAX_VALUE-8?
  7. ffmpeg 源码学习之seek play
  8. 利用赫夫曼编码进行数据解压
  9. cocos2dx libevent简介和使用
  10. 第 3 章 镜像 - 018 - 镜像命名的最佳实践
  11. 优酷直播节目“冠军体育课”第二季收官 共吸引近百万用户观看互动
  12. Ubuntu下远程访问MySQL数据库
  13. 51单片机 模块化编程
  14. AGND为模拟地,DGND为数字地
  15. Max Core Frequency 异常显示为-1.80GHz -- Intel-Extreme-Tuning-Utility-Intel-XTU (英特尔 XTU)
  16. cad找形心插件 lisp_晓东CAD家园-论坛-A/VLISP-[LISP程序]:求形心-谁有求形心的LISP.帮帮忙。时在是太累啦。 - Powered by Discuz!...
  17. 将高分三号中的SAR图可视化处理
  18. addr2line来定位问题
  19. 【托业】【新托业TOEIC新题型真题】学习笔记5-题库二-P7
  20. 【唐老狮】Unity和UE4两大游戏引擎,你该如何选择?

热门文章

  1. 倾斜摄影实景三维建模效果不好?可能有这些原因!
  2. SpringBoot+Dubbo实战demo
  3. 在Zuul中设置服务访问限流
  4. Windows对象管理器
  5. Mirantis决定采用Kubernetes作为 Openstack的生命周期管理工具
  6. 字符串与数组之间的相互转化
  7. iOS优化内存,提升性能 之五
  8. Visual Studio 2012 Ultimate旗舰版序列号
  9. 在 Ubuntu 14.04 中安装 Pepper Flash Player For Chromium-转
  10. 蓝桥杯 BASIC-29 基础练习 高精度加法