Golang语言标准库 sync 包的 WaitGroup 怎么使用?

简介: Golang语言标准库 sync 包的 WaitGroup 怎么使用?

image.jpeg

01

介绍


在 Go 语言中,标准库 sync 包的 WaitGroup 用于父线程(goroutine)等待一组子线程(goroutine结束,如果正在执行的一组子线程还没有全部结束,父线程阻塞在检查点,直到所有子线程全部结束才可以继续执行。


02

基本使用


WaitGroup 提供了 3 个方法,Add、Done 和 Wait,下面分别介绍一下这 3 个方法:

  • Add(delta int):用于设置 WaitGroup 计数器的值。
  • Done():用于将 WaitGroup 计数器的值减 1.
  • Wait():
    用于阻塞调用 Wait() 方法的 goroutine,直到 WaitGroup 计数器的值为 0。


我们通过并发计数的代码示例,演示 WaitGroup 的 3 个方法的基本使用:


640.png


阅读并发计数代码,我们可以发现程序通过启动 10 个 goroutine,并发执行计数。

  • 第 20 行,声明 WaitGroup 变量,初始值为 0。
  • 第 21 行,设置 WaitGroup 计数器的值为 10,因为我们编排 10 个 goroutine 并发执行计数代码。
  • 第 24 行,每个 goroutine 执行结束,使用 defer 调用 Done 方法,将 WaitGroup 计数器的值减 1。
  • 第 28 行,通过调用 Wait 方法,检查子 goroutine 是否全部结束,决定父 goroutine 是否继续执行。


03

实现原理



type WaitGroup struct {
  noCopy noCopy
  state1 [3]uint32
}


阅读源码,可以发现 WaitGroup 包含两个字段,noCopy 字段是用来辅助 vet检查该 WaitGroup 是否是通过 Copy 赋值,state1 字段是用来记录 WaitGroup 的计数器值、信号量和阻塞的 waiter 数量。


WaitGroup 的 Add(delta int) 方法,主要就是操作 state1,传入参数 delta,程序将 delta 的值加到计数器上,delta 的值可以为负值,Done 方法就是调用 Add(-1) 实现的,但是不建议大家传负值使用 Add 方法。


WaitGroup 的 Wait 方法的实现逻辑是,不断检查 state 的值,如果发现 state 的值为 0,说明所有 goroutine 都已经结束,Wait 方法的调用者可以继续执行,如果发现 state 的值不为 0,说明还有 goroutine 没有结束,Wait 方法的调用者需要阻塞。


04

踩坑


WaitGroup 计数器的值必须大于等于 0,如果计数器的值小于 0,会导致程序 panic。所以,我们在使用的时候,不建议给 Add(delta int) 方法delta 参数传递负值。并且还必须保证两点,一是设置计数器的值与 goroutine 的数量一致,二是调用 Done 方法的次数与 goroutine 的数量一致。


05

总结


本文开篇先介绍了 WaitGroup 的作用,接着通过并发计数的代码示例,演示了 WaitGroup 的 3 个方法如何使用,然后介绍了 WaitGroup 的 3 个方法的实现原理,最后列举了一个非常容易踩的「坑」。





目录
相关文章
|
2月前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
117 4
Golang语言之管道channel快速入门篇
|
2月前
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
104 3
Golang语言之gRPC程序设计示例
|
3天前
|
JSON Go 开发者
go-carbon v2.5.0 发布,轻量级、语义化、对开发者友好的 golang 时间处理库
carbon 是一个轻量级、语义化、对开发者友好的 Golang 时间处理库,提供了对时间穿越、时间差值、时间极值、时间判断、星座、星座、农历、儒略日 / 简化儒略日、波斯历 / 伊朗历的支持。
21 4
|
21天前
|
存储 Cloud Native Shell
go库介绍:Golang中的Viper库
Viper 是 Golang 中的一个强大配置管理库,支持环境变量、命令行参数、远程配置等多种配置来源。本文详细介绍了 Viper 的核心特点、应用场景及使用方法,并通过示例展示了其强大功能。无论是简单的 CLI 工具还是复杂的分布式系统,Viper 都能提供优雅的配置管理方案。
|
2月前
|
Prometheus Cloud Native Go
Golang语言之Prometheus的日志模块使用案例
这篇文章是关于如何在Golang语言项目中使用Prometheus的日志模块的案例,包括源代码编写、编译和测试步骤。
56 3
Golang语言之Prometheus的日志模块使用案例
|
1月前
|
前端开发 中间件 Go
实践Golang语言N层应用架构
【10月更文挑战第2天】本文介绍了如何在Go语言中使用Gin框架实现N层体系结构,借鉴了J2EE平台的多层分布式应用程序模型。文章首先概述了N层体系结构的基本概念,接着详细列出了Go语言中对应的构件名称,包括前端框架(如Vue.js、React)、Gin的处理函数和中间件、依赖注入和配置管理、会话管理和ORM库(如gorm或ent)。最后,提供了具体的代码示例,展示了如何实现HTTP请求处理、会话管理和数据库操作。
33 0
|
2月前
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
67 4
Golang语言文件操作快速入门篇
|
2月前
|
安全 Go
Golang语言goroutine协程并发安全及锁机制
这篇文章是关于Go语言中多协程操作同一数据问题、互斥锁Mutex和读写互斥锁RWMutex的详细介绍及使用案例,涵盖了如何使用这些同步原语来解决并发访问共享资源时的数据安全问题。
88 4
|
2月前
|
Go
Golang语言错误处理机制
这篇文章是关于Golang语言错误处理机制的教程,介绍了使用defer结合recover捕获错误、基于errors.New自定义错误以及使用panic抛出自定义错误的方法。
50 3
|
2月前
|
Go 调度
Golang语言goroutine协程篇
这篇文章是关于Go语言goroutine协程的详细教程,涵盖了并发编程的常见术语、goroutine的创建和调度、使用sync.WaitGroup控制协程退出以及如何通过GOMAXPROCS设置程序并发时占用的CPU逻辑核心数。
53 4
Golang语言goroutine协程篇