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 个方法的实现原理,最后列举了一个非常容易踩的「坑」。





目录
相关文章
|
7月前
|
Java 编译器 Go
【Golang】(1)Go的运行流程步骤与包的概念
初次上手Go语言!先来了解它的运行流程吧! 在Go中对包的概念又有怎样不同的见解呢?
365 4
|
7月前
|
存储 安全 Java
【Golang】(4)Go里面的指针如何?函数与方法怎么不一样?带你了解Go不同于其他高级语言的语法
结构体可以存储一组不同类型的数据,是一种符合类型。Go抛弃了类与继承,同时也抛弃了构造方法,刻意弱化了面向对象的功能,Go并非是一个传统OOP的语言,但是Go依旧有着OOP的影子,通过结构体和方法也可以模拟出一个类。
374 2
|
10月前
|
设计模式 Kubernetes Go
​​什么是Golang项目的“主包精简,逻辑外置”?​
“主包精简,逻辑外置”是Go语言项目的一种设计原则,强调将程序入口保持简单,核心逻辑拆分至其他包,以提升代码可维护性、可测试性及扩展性,适用于CLI工具、Web服务等场景。
225 7
|
Go
在golang中发起http请求以获取访问域名的ip地址实例(使用net, httptrace库)
这只是追踪我们的行程的简单方法,不过希望你跟着探险家的脚步,即使是在互联网的隧道中,也可以找到你想去的地方。接下来就是你的探险之旅了,祝你好运!
621 26
|
Go 开发者
go-carbon v2.6.0 重大版本更新,轻量级、语义化、对开发者友好的 golang 时间处理库
carbon 是一个轻量级、语义化、对开发者友好的 Golang 时间处理库,提供了对时间穿越、时间差值、时间极值、时间判断、星座、星座、农历、儒略日 / 简化儒略日、波斯历 / 伊朗历的支持
299 3
|
网络协议 测试技术 Linux
Golang 实现轻量、快速的基于 Reactor 模式的非阻塞 TCP 网络库
gev 是一个基于 epoll 和 kqueue 实现的高性能事件循环库,适用于 Linux 和 macOS(Windows 暂不支持)。它支持多核多线程、动态扩容的 Ring Buffer 读写缓冲区、异步读写和 SO_REUSEPORT 端口重用。gev 使用少量 goroutine,监听连接并处理读写事件。性能测试显示其在不同配置下表现优异。安装命令:`go get -u github.com/Allenxuxu/gev`。
369 0
|
JSON Go 开发者
go-carbon v2.5.0 发布,轻量级、语义化、对开发者友好的 golang 时间处理库
carbon 是一个轻量级、语义化、对开发者友好的 Golang 时间处理库,提供了对时间穿越、时间差值、时间极值、时间判断、星座、星座、农历、儒略日 / 简化儒略日、波斯历 / 伊朗历的支持。
350 4
|
存储 Cloud Native Shell
go库介绍:Golang中的Viper库
Viper 是 Golang 中的一个强大配置管理库,支持环境变量、命令行参数、远程配置等多种配置来源。本文详细介绍了 Viper 的核心特点、应用场景及使用方法,并通过示例展示了其强大功能。无论是简单的 CLI 工具还是复杂的分布式系统,Viper 都能提供优雅的配置管理方案。
504 6
|
前端开发 中间件 Go
实践Golang语言N层应用架构
【10月更文挑战第2天】本文介绍了如何在Go语言中使用Gin框架实现N层体系结构,借鉴了J2EE平台的多层分布式应用程序模型。文章首先概述了N层体系结构的基本概念,接着详细列出了Go语言中对应的构件名称,包括前端框架(如Vue.js、React)、Gin的处理函数和中间件、依赖注入和配置管理、会话管理和ORM库(如gorm或ent)。最后,提供了具体的代码示例,展示了如何实现HTTP请求处理、会话管理和数据库操作。
340 1
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
806 4
Golang语言之管道channel快速入门篇