当前位置:网站首页>Golang sync.WaitGroup
Golang sync.WaitGroup
2022-08-03 13:16:00 【Cloud full of notes】
1. Golang sync.WaitGroup
1.1. 基础知识
This is through the channel, 来控制 goroutine Example of coroutine ending:
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}
We learned in the previous section, sign When the channel reads data, 如果命中"有缓冲 channel + 缓冲为空"的情况, 会阻塞, 只有两个 go The coroutine is all executed, 往 sign After plugging the data, 程序才会退出, 但是这种方式非常繁琐.在这种应用场景下, 我们可以选用另外一个同步工具 sync.WaitGroup(以下简称 WaitGroup 类型), 它比通道更加适合实现这种一对多的 goroutine 协作流程.WaitGroup 类型是开箱即用的, 也是并发安全的, It has three pointer methods: Add、Done 和 Wait, 你可以想象该类型中有一个计数器, 它的默认值是 0, 我们可以通过调用该类型值的 Add 方法来增加, 或者减少这个计数器的值, The code upgrade is as follows:
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, Then it will wake up}
Add 会增加计数器的值, Done will decrement the value of the counter, Wait 会一直阻塞, until the value of the counter returns to 0, and then wake up, Continue to execute backwards.
1.2. 常见的坑
如果使用不当, 容易抛出 Panic, I will list the relevant knowledge points:
- 坑 1(The counter is negative): sync.WaitGroup The value of the counter in type value if less than 0, 会直接抛出 Panic.
- 坑 2(同时调用 Add 和 Wait): 如果我们对它的 Add 方法的首次调用, 与对它的 Wait 方法的调用是同时发起的, 比如, 在同时启用的两个 goroutine 中, 分别调用这两个方法, 那么就有可能会让这里的 Add 方法抛出一个 panic.
- 坑 3(spanning count cycles): 如果在一个此类值的 Wait 方法被执行期间, 跨越了两个计数周期, 那么就会引发一个 panic.
对于坑 1, 当调用 Add 方法, May appear when a negative number is passed in, 所以我们使用 WaitGroup 时, Need to ensure that the count is always greater than 0.对于坑 2, 需要说明一点, 虽然 WaitGroup 值本身并不需要初始化, 但是尽早地增加其计数器的值, 还是非常有必要的.对于坑 3, 我们需要先了解 WaitGroup 的计数周期:
计数周期: WaitGroup The medium counter value is given by 0 变为了某个正整数, 而后又经过一系列的变化, 最终由某个正整数又变回了 0.也就是说, 只要计数器的值始于 0 又归为 0, 就可以被视为一个计数周期.在一个此类值的生命周期中, 它可以经历任意多个计数周期.但是, 只有在它走完当前的计数周期之后, 才能够开始下一个计数周期.that pit 3 What will happen? 场景如下: 当前的 goroutine 因调用 Wait when the method is blocked, 另一个 goroutine 调用了该值的 Done 方法, 并使其计数器的值变为了 0, 这会唤醒当前的 goroutine, 并使它试图继续执行 Wait 方法中其余的代码.但在这时, 又有一个 goroutine 调用了它的 Add 方法, 并让其计数器的值又从 0 变为了某个正整数.此时, 这里的 Wait 方法就会立即抛出一个 panic.According to the pit 2 和坑 3, 总结如下: 不要把增加其计数器值的操作和调用其 Wait 方法的代码, 放在不同的 goroutine 中执行.换句话说, 要杜绝对同一个 WaitGroup 值的两种操作的并发执行, The standard way should be"先统一 Add, 再并发 Done, 最后 Wait".
1.3. 并发实例: Push
For the concurrency example from the previous chapter, A question was raised at the time: 每消费一条 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, Then it will wake up// 数据统计 SendNotify(t.ID, t.TotalPage, t.TaskPage, t.AppType, t.AppLocal, fails, succs)
1.4. 总结
WaitGroup is out-of-the-box and concurrency-safe, One-to-many can be easily achieved through it goroutine 协作流程, 即: 一个分发子任务的 goroutine, 和多个执行子任务的 goroutine, 共同来完成一个较大的任务.在使用 WaitGroup 值的时候, 我们一定要注意, 千万不要让其中的计数器的值小于 0, 否则就会引发 panic.另外, 我们最好用"先统一 Add, 再并发 Done, 最后 Wait"这种标准方式, 来使用 WaitGroup 值, 尤其不要在调用 Wait 方法的同时, 并发地通过调用 Add 方法去增加其计数器的值, 因为这也有可能引发 panic.
边栏推荐
- An动画优化之遮罩层动画
- [Deep Learning] Overview of Efficient and Lightweight Semantic Segmentation
- leetcode/字符串中的所有变位词(s1字符串的某个排列是s2的子串)的左索引
- An animation based button animation combined with basic code
- Graphic animation and button animation of an animation basic component
- 海外代购系统/代购网站怎么搭建——源码解析
- 【OpenCV】 书本视图矫正 + 广告屏幕切换 透视变换图像处理
- d写二进制
- 使用工作队列管理器(三)
- self-discipline
猜你喜欢
Oracle安装完毕(系统盘),从系统盘转移到数据盘
什么是分布式锁?几种分布式锁分别是怎么实现的?
论文理解:“Gradient-enhanced physics-informed neural networks for forwardand inverse PDE problems“
An动画优化之补间形状与传统补间的优化
An动画优化之遮罩层动画
【深度学习】高效轻量级语义分割综述
An工具介绍之宽度工具、变形工具与套索工具
An animation optimization of traditional guide layer animation
图像融合SDDGAN文章学习
Win11怎么禁止软件后台运行?Win11系统禁止应用在后台运行的方法
随机推荐
【深度学习】高效轻量级语义分割综述
查看GCC版本_qt版本
基于php网上零食商店管理系统获取(php毕业设计)
Grafana 高可用部署最佳实践
Graphic animation and button animation of an animation basic component
ECCV 2022 | AirDet: 无需微调的小样本目标检测方法
便携烙铁开源系统IronOS,支持多款便携DC, QC, PD供电烙铁,支持所有智能烙铁标准功能
浅谈低代码平台远程组件加载方案
论文理解:“Gradient-enhanced physics-informed neural networks for forwardand inverse PDE problems“
来广州找工作有一个多月了,今天终于有着落了,工资7000
leetcode 11. The container that holds the most water
Yahoo! Answers-数据集
Nodejs 安装依赖cpnm时,install 出现Error: Cannot find module ‘fs/promises‘
self-discipline
Jmeter use
Jmeter使用
An animation optimization of traditional guide layer animation
【R】用grafify搞定统计绘图、方差分析、干预比较等!
什么是分布式锁?几种分布式锁分别是怎么实现的?
云计算服务主要安全风险及应对措施初探