当前位置:网站首页>Golang sync.WaitGroup
Golang sync.WaitGroup
2022-08-03 12:47:00 【云满笔记】
1. Golang sync.WaitGroup
1.1. 基础知识
这个是通过通道, 来控制 goroutine 协程结束的示例:
func coordinateWithChan() {
sign := make(chan struct{
}, 2) num := int32(0) fmt.Printf("The number: %d [with chan struct{}]\n", num) max := int32(10) go addNum(&num, 1, max, func() {
sign <- struct{
}{
} }) go addNum(&num, 2, max, func() {
sign <- struct{
}{
} }) <-sign <-sign}
上一节我们学习过, sign 通道读取数据时, 如果命中"有缓冲 channel + 缓冲为空"的情况, 会阻塞, 只有两个 go 协程全部执行完毕, 往 sign 塞数据后, 程序才会退出, 但是这种方式非常繁琐。在这种应用场景下, 我们可以选用另外一个同步工具 sync.WaitGroup(以下简称 WaitGroup 类型), 它比通道更加适合实现这种一对多的 goroutine 协作流程。WaitGroup 类型是开箱即用的, 也是并发安全的, 它拥有三个指针方法: Add、Done 和 Wait, 你可以想象该类型中有一个计数器, 它的默认值是 0, 我们可以通过调用该类型值的 Add 方法来增加, 或者减少这个计数器的值, 代码升级如下:
func coordinateWithWaitGroup() {
var wg sync.WaitGroup wg.Add(2) // 计数器加 2 num := int32(0) fmt.Printf("The number: %d [with sync.WaitGroup]\n", num) max := int32(10) go addNum(&num, 3, max, wg.Done) // 计数器减 1 go addNum(&num, 4, max, wg.Done) // 计数器减 1 wg.Wait() // 会阻塞, 直到计数器值为 0, 然后就会被唤醒}
Add 会增加计数器的值, Done 会减少计数器的值, Wait 会一直阻塞, 直到计数器的值重新回归为 0, 然后才会被唤醒, 继续往后面执行。
1.2. 常见的坑
如果使用不当, 容易抛出 Panic, 我就把相关知识点列出来:
- 坑 1(计数器为负数): sync.WaitGroup 类型值中计数器的值如果小于 0, 会直接抛出 Panic。
- 坑 2(同时调用 Add 和 Wait): 如果我们对它的 Add 方法的首次调用, 与对它的 Wait 方法的调用是同时发起的, 比如, 在同时启用的两个 goroutine 中, 分别调用这两个方法, 那么就有可能会让这里的 Add 方法抛出一个 panic。
- 坑 3(跨越计数周期): 如果在一个此类值的 Wait 方法被执行期间, 跨越了两个计数周期, 那么就会引发一个 panic。
对于坑 1, 当调用 Add 方法, 传入一个负数的时候可能会出现, 所以我们使用 WaitGroup 时, 需要保证计数一直大于 0。对于坑 2, 需要说明一点, 虽然 WaitGroup 值本身并不需要初始化, 但是尽早地增加其计数器的值, 还是非常有必要的。对于坑 3, 我们需要先了解 WaitGroup 的计数周期:
计数周期: WaitGroup 中计数器值由 0 变为了某个正整数, 而后又经过一系列的变化, 最终由某个正整数又变回了 0。也就是说, 只要计数器的值始于 0 又归为 0, 就可以被视为一个计数周期。在一个此类值的生命周期中, 它可以经历任意多个计数周期。但是, 只有在它走完当前的计数周期之后, 才能够开始下一个计数周期。那坑 3 什么情况会出现呢? 场景如下: 当前的 goroutine 因调用 Wait 方法被阻塞的时候, 另一个 goroutine 调用了该值的 Done 方法, 并使其计数器的值变为了 0, 这会唤醒当前的 goroutine, 并使它试图继续执行 Wait 方法中其余的代码。但在这时, 又有一个 goroutine 调用了它的 Add 方法, 并让其计数器的值又从 0 变为了某个正整数。此时, 这里的 Wait 方法就会立即抛出一个 panic。根据坑 2 和坑 3, 总结如下: 不要把增加其计数器值的操作和调用其 Wait 方法的代码, 放在不同的 goroutine 中执行。换句话说, 要杜绝对同一个 WaitGroup 值的两种操作的并发执行, 标准方式应该为"先统一 Add, 再并发 Done, 最后 Wait"。
1.3. 并发实例: Push
对于上一章的并发示例, 当时提了一个问题: 每消费一条 Channel 数据, 需要记录 Push 发送成功, 但是一条 Channel 数据包含 2-3 个 Push 内容 (IOS/Android/PC), 程序记录 Push 成功前, 如何保证这 2-3 个 Push 都发送完毕了呢? 根据"先统一 Add, 再并发 Done, 最后 Wait"原则, 看下面代码:
var ( wg sync.WaitGroup succs []*NotifyMessage fails []*NotifyMessage)for _, message := range t.PushMessages {
wg.Add(1) // 计数加 1 go func(message mipush.PushMessage) { defer func() { wg.Done() // 计数减 1 }() // 发送 IOS/Android/PC 等渠道的 Push // 代码省略。.. }(message)}wg.Wait() // 阻塞, 直到计数器值为 0, 然后就会被唤醒// 数据统计 SendNotify(t.ID, t.TotalPage, t.TaskPage, t.AppType, t.AppLocal, fails, succs)
1.4. 总结
WaitGroup 是开箱即用和并发安全的, 可以通过它很方便地实现一对多 goroutine 协作流程, 即: 一个分发子任务的 goroutine, 和多个执行子任务的 goroutine, 共同来完成一个较大的任务。在使用 WaitGroup 值的时候, 我们一定要注意, 千万不要让其中的计数器的值小于 0, 否则就会引发 panic。另外, 我们最好用"先统一 Add, 再并发 Done, 最后 Wait"这种标准方式, 来使用 WaitGroup 值, 尤其不要在调用 Wait 方法的同时, 并发地通过调用 Add 方法去增加其计数器的值, 因为这也有可能引发 panic。
边栏推荐
- An动画基础之元件的影片剪辑动画与传统补间
- php microtime 封装工具类,计算接口运行时间(打断点)
- Byte's favorite puzzle questions, how many do you know?
- Random forest project combat - temperature prediction
- 利用pgsql插件PostGIS 实现地理坐标系数据转换
- 链游NFT元宇宙游戏系统开发技术方案及源码
- When Nodejs installation depends on cpnm, the install shows Error: Cannot find module 'fs/promises'
- setTimeout, setInterval requestAnimationFrame
- Notepad++ 安装jsonview插件
- Nodejs 安装依赖cpnm时,install 出现Error: Cannot find module ‘fs/promises‘
猜你喜欢
An动画优化之补间形状与传统补间的优化
业界新标杆!阿里开源自研高并发编程核心笔记(2022最新版)
Image fusion GAN-FM study notes
Yahoo!Answers - data set
Random forest project combat - temperature prediction
YOLOv5训练数据提示No labels found、with_suffix使用、yolov5训练时出现WARNING: Ignoring corrupted image and/or label
基于php旅游网站管理系统获取(php毕业设计)
Real number rounding and writing to file (C language file)
图像融合GAN-FM学习笔记
shell编程条件语句
随机推荐
软件测试自学还是报班好?
How to build an overseas purchasing system/purchasing website - source code analysis
(through page) ali time to upload the jar
d作者:d的新特性
Redis连接池工具类
类和对象(中上)
PolarFormer: Multi-camera 3D Object Detection with Polar Transformers 论文笔记
leetcode 11. The container that holds the most water
免费的网络传真平台_发传真不显示发送号码
AMS simulation
An动画基础之散件动画原理与形状提示点
Comics: how do you prove that sleep does not release the lock, and wait to release lock?
自律成就自己
基于php校园医院门诊管理系统获取(php毕业设计)
When Nodejs installation depends on cpnm, the install shows Error: Cannot find module 'fs/promises'
ECCV 2022|通往数据高效的Transformer目标检测器
SQL分页查询_Sql根据某个字段分页
Mysql重启后innodb和myisam插入的主键id变化总结
An动画基础之元件的影片剪辑动画与传统补间
第十五章 源代码文件 REST API 简介