go sync包(五) WaitGroup

WaitGroup

sync.WaitGroup 可以等待一组 Goroutine 的返回,一个比较常见的使用场景是批量发出 RPC 或者 HTTP 请求:

requests := []*Request{...}
wg := &sync.WaitGroup{}
wg.Add(len(requests))

for _, request := range requests {
    go func(r *Request) {
        defer wg.Done()
        // res, err := service.call(r)
    }(request)
}
wg.Wait()
// In the terminology of the Go memory model, a call to Done
// “synchronizes before” the return of any Wait call that it unblocks.
type WaitGroup struct {
   noCopy noCopy

   // 64-bit value: high 32 bits are counter, low 32 bits are waiter count.
   // 64-bit atomic operations require 64-bit alignment, but 32-bit
   // compilers only guarantee that 64-bit fields are 32-bit aligned.
   // For this reason on 32 bit architectures we need to check in state()
   // if state1 is aligned or not, and dynamically "swap" the field order if
   // needed.
   state1 uint64
   state2 uint32
}
  • noCopy:禁止拷贝。
  • state:state 是 WaitGroup 的状态数据字段,且是一个无符号64 bit的数据,内容包含了 counter , waiter 信息
    • counter 代表目前尚未完成的个数,WaitGroup.Add(n) 将会导致 counter += n, 而 WaitGroup.Done() 将导致 counter–。
    • waiter 代表目前已调用 WaitGroup.Wait 的 goroutine 的个数。

在这里插入图片描述

Add

// Add adds delta, which may be negative, to the WaitGroup counter.
// If the counter becomes zero, all goroutines blocked on Wait are released.
// If the counter goes negative, Add panics.
//
// Note that calls with a positive delta that occur when the counter is zero
// must happen before a Wait. Calls with a negative delta, or calls with a
// positive delta that start when the counter is greater than zero, may happen
// at any time.
// Typically this means the calls to Add should execute before the statement
// creating the goroutine or other event to be waited for.
// If a WaitGroup is reused to wait for several independent sets of events,
// new Add calls must happen after all previous Wait calls have returned.
// See the WaitGroup example.
func (wg *WaitGroup) Add(delta int) {
   statep, semap := wg.state()
   if race.Enabled {
      if delta < 0 {
         // Synchronize decrements with Wait.
         race.ReleaseMerge(unsafe.Pointer(wg))
      }
      race.Disable()
      defer race.Enable()
   }
   // counter 增加数量
   state := wg.state.Add(uint64(delta) << 32)
   // 取出 counter
   v := int32(state >> 32)
   // 取出 waiter
   w := uint32(state)
   if race.Enabled && delta > 0 && v == int32(delta) {
      // The first increment must be synchronized with Wait.
      // Need to model this as a read, because there can be
      // several concurrent wg.counter transitions from 0.
      race.Read(unsafe.Pointer(&wg.sema))
   }
   // counter < 0,panic
   if v < 0 {
      panic("sync: negative WaitGroup counter")
   }
   // delta > 0 && v == int32(delta) : 表示从 0 开始添加计数值
   // w!=0 :表示已经有了等待者
   // 说明说明在调用了 Wait() 方法之后又想加入新的等待者,这种操作是不允许的
   if w != 0 && delta > 0 && v == int32(delta) {
      panic("sync: WaitGroup misuse: Add called concurrently with Wait")
   }
   if v > 0 || w == 0 {
      return
   }
   // This goroutine has set counter to 0 when waiters > 0.
   // Now there can't be concurrent mutations of state:
   // - Adds must not happen concurrently with Wait,
   // - Wait does not increment waiters if it sees counter == 0.
   // Still do a cheap sanity check to detect WaitGroup misuse.
   // 避免并发调用 Add() 和 Wait()
   if *statep != state {
      panic("sync: WaitGroup misuse: Add called concurrently with Wait")
   }
   // Reset waiters count to 0.
   // 唤醒所有 waiter
   *statep = 0
   for ; w != 0; w-- {
      runtime_Semrelease(semap, false, 0)
   }
}

Wait

// Wait blocks until the WaitGroup counter is zero.
func (wg *WaitGroup) Wait() {
   statep, semap := wg.state()
   if race.Enabled {
      _ = *statep // trigger nil deref early 
      race.Disable()
   }
   for {
      // 读取state
      // 高32位 ==> counter
      // 低32位 ==> waiter
      state := atomic.LoadUint64(statep)
      v := int32(state >> 32)
      w := uint32(state)
      // counter减到0了,return
      if v == 0 {
         // Counter is 0, no need to wait.
         if race.Enabled {
            race.Enable()
            race.Acquire(unsafe.Pointer(wg))
         }
         return
      }
      // Increment waiters count.
      // counter 不为0,waiters++
      if wg.state.CompareAndSwap(state, state+1) {
         if race.Enabled && w == 0 {
            // Wait must be synchronized with the first Add.
            // Need to model this is as a write to race with the read in Add.
            // As a consequence, can do the write only for the first waiter,
            // otherwise concurrent Waits will race with each other.
            race.Write(unsafe.Pointer(semap))
         }
         // 阻塞
         runtime_Semacquire(semap)
         // 被唤醒,检查state是否等于0
         // 不为0,说明存在计数值未恢复为0就重用,panic
         if *statep != 0 {
            panic("sync: WaitGroup is reused before previous Wait has returned")
         }
         if race.Enabled {
            race.Enable()
            race.Acquire(unsafe.Pointer(wg))
         }
         return
      }
   }
}

Done

// Done decrements the WaitGroup counter by one.
func (wg *WaitGroup) Done() {
   wg.Add(-1)
}

Done 只是调用了 Add() ,将groutine - 1。

小结

  • sync.WaitGroup 必须在 sync.WaitGroup.Wait 方法返回之后才能被重新使用。
  • sync.WaitGroup.Done 只是对 sync.WaitGroup.Add 方法的简单封装,我们可以向 sync.WaitGroup.Add 方法传入任意负数(需要保证计数器非负)快速将计数器归零以唤醒等待的 Goroutine。
  • 可以同时有多个 Goroutine 等待当前 sync.WaitGroup 计数器的归零,这些 Goroutine 会被同时唤醒。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/752533.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

IBM Spectrum LSF RTM,针对 IBM Spectrum LSF 环境的高级报告、跟踪和监控工具

亮点 为 IBM Spectrum LSF 环境提供性能监控和报告 监控 FlexNet Publisher 和 Reprisebased 许可证的使用&#xff0c;提供详细和总结性报告 通过多个级别&#xff08;包括应用程序和组织&#xff09;的报告来监控共享存储的利用率 提供强大的生产力工具&#xff0c;包括操…

计算机公共课面试常见问题:线性代数篇

目录 1. 特征向量和特征值代表什么含义&#xff1f; 2. 矩阵的秩是什么&#xff1f;满秩代表什么&#xff1f;不满秩呢&#xff1f; 3. 奇异值分解是什么&#xff1f; …

二手房和新房市场冰火两重天!原因为何?杭州给出了答案

随着房市的回暖&#xff0c;也出现了不少的不利的消息&#xff0c;一边二手房被抢购&#xff0c;一边是新房难卖&#xff0c;为何如此矛盾&#xff1f;近期杭州的楼市行情给出了答案。 5月份在诸多政策的刺激下&#xff0c;杭州楼市保持了热度&#xff0c;不过却有点凉风&#…

“论云上自动化运维及其应用”写作框架,软考高级,系统架构设计师

论文真题 云上自动化运维是传统IT运维和DevOps的延伸&#xff0c;通过云原生架构实现运维的再进化。云上自动化运维可以有效帮助企业降低IT运维成本&#xff0c;提升系统的灵活度&#xff0c;以及系统的交付速度&#xff0c;增强系统的可靠性&#xff0c;构建更加安全、可信、…

【SGX系列教程】(三)Intel-SGX 官方示例分析(SampleCode)——Cxx11SGXDemo

文章目录 示例一. Cxx11SGXDemo1.1 README1.2 重点代码分析1.2.1 App/App.cpp1.2.2 App/TrustedLibrary/Libcxx.cpp1.2.3 Enclave.edl1.2.4 Enclave/TrustedLibrary/Libcxx.cpp1.2.5 Enclave/Enclave.cppEnclave.h 1.3 编译执行1.4 总结 示例二. Cxx14SGXDemo示例三. Cxx17SGXD…

SpringBoot集成道历(实现道历日期查询)

官网地址&#xff1a;官网地址https://6tail.cn/calendar/api.html 1、导入依赖 <dependency><groupId>cn.6tail</groupId><artifactId>lunar</artifactId><version>1.3.9</version></dependency><dependency><group…

斜光测距的原理及python实现

1.前言 最近做了一个基于opencv的斜光测距的小项目&#xff0c;东西不多&#xff0c;但是很有意思&#xff0c;值得拿出来学一学。项目里面需要比较精确的定位功能&#xff0c;将前人matlab代码移植到python上&#xff0c;并且做了一些优化&#xff0c;简化逻辑(毕竟我是专业的…

uView 2.0:uni-app生态的利剑出鞘,引领UI框架新纪元

引言 随着移动互联网的快速发展&#xff0c;跨平台应用开发成为了开发者们关注的焦点。uni-app&#xff0c;一个基于Vue.js的跨平台应用开发框架&#xff0c;因其高效、易用的特性而广受欢迎。在uni-app的生态系统中&#xff0c;UI框架的选择对于开发者而言至关重要。今天&…

AI 编程探索- iOS动态标签控件

需求分析&#xff1a; 标签根据文字长度&#xff0c;自适应标签居中显示扩展 超过内容显示范围&#xff0c;需要换行显示&#xff0c;且保持居中显示 AI实现过程 提问&#xff1a; 回答&#xff1a; import UIKit import SnapKitclass DynamicLabelsContainerView: UIView…

docker 多网卡指定网卡出网

前言 宿主机中有多个网卡 ens160 192.168.4.23/20 内网通信用 ens192 10.31.116.128/24 出公网访问-1 ens193 10.31.116.128/24 出公网访问-2 现在需要不同容器中不同出网访问&#xff0c;举例 容器1 192.168.0.1/20 网段走宿主机 ens160网卡&#xff0c;否则全部走ens192 网…

CAS自旋解析

CAS全称CompareAndSwap(比较并交换)&#xff0c;是cpu的指令&#xff0c;调用时不涉及上下文的切换。Java中属于乐观锁的一种&#xff0c;具体流程如下图&#xff1a; 具体的实现使用的是Unsafe类去调用native修饰的compareAndSwap方法&#xff0c;4个字段分别是对象实例&#…

Shell编程实战

脚本编程步骤 脚本编程一般分为以下几个步骤: 需求分析:根据系统管理的需求&#xff0c;分析脚本要实现的功能、功能实现的层次、实现的命令与语句等; 命令测试:将要用到的命令逐个进行测试&#xff0c;以决定使用的选项、要设置的变量等: 脚本编程:将测试好的命令写入到脚本文…

庆祝东兴市金顺心贸易有限公司代理越南三原竹系列产品五周年

&#x1f389;庆祝金顺心贸易代理越南三原竹系列产品五周年&#xff01;这五年&#xff0c;我们共同成长&#xff0c;每一份产品都承载着越南的美味与匠心。感恩有你们&#xff0c;未来的路&#xff0c;我们继续携手前行&#xff0c;品味更多美好&#xff01;&#x1f962;&…

电子名片小程序源码系统 前后端分离 带完整的安装代码包以及搭建教程

系统概述 电子名片小程序源码系统是一款基于前后端分离架构的综合性平台&#xff0c;旨在为用户提供一个集销售名片和企业商城于一体的解决方案。该系统采用先进的技术手段&#xff0c;实现了个性化名片设计、便捷的销售功能、企业商城模块等一系列实用功能。同时&#xff0c;…

vue-cli 搭建项目

创建 router 目录 在一个.js文件中添加 打开外部命令 打开外部命令后&#xff0c;在指令栏输入npm i vue-router3.5.3 &#xff0c;等待下载 下载完成后 在 main.js 中配置路由 输入这些后&#xff0c;基本的配置就实现了 最后进行测试&#xff0c;验证是否配置 或者打开外部命…

springcloud第4季 分布式事务seata作用服务搭建1

一 seata作用 1.1 seata简介 1.seata是一款解决分布式事务的解决方案&#xff0c;致力于在微服务架构下提供高性能和简单易用的分布式事务服务。 1.2 seata的术语 一个中心&#xff1a;全局事务id&#xff0c;xid&#xff0c;在调用服务链路的上下文中进行传播。TC(Transa…

iPhone怎么恢复删除的数据?几款顶级iPhone数据恢复软件

从iOS设备恢复数据。 对于任何数据恢复软件来说&#xff0c;从iOS设备恢复数据都是一项复杂的任务&#xff0c;因为Apple已将众多数据保护技术集成到现代iPhone和iPad中。其中包括硬件加密和文件级加密。iOS 上已删除的数据只能通过取证文件工件搜索来找到&#xff0c;例如分析…

最新扣子(Coze)实战案例:图像流工具之空间风格化,完全免费教程

&#x1f9d9;‍♂️ 大家好&#xff0c;我是斜杠君&#xff0c;手把手教你搭建扣子AI应用。 &#x1f4dc; 本教程是《AI应用开发系列教程之扣子(Coze)实战教程》&#xff0c;完全免费学习。 &#x1f440; 关注斜杠君&#xff0c;可获取完整版教程。&#x1f44d;&#x1f3f…

EHS,制造业安全绿色生产的隐形守护神

当我们提到EHS&#xff0c;可能很多人会稍感陌生&#xff0c;毕竟它不是一个日常生活中经常提及的词汇。但实际上&#xff0c;EHS在我们的生活和工作中扮演着极其重要的角色&#xff0c;尤其对制造业而言更是可持续发展经营管理的重中之重。 一、EHS是什么意思&#xff1f; E…

vue项目内网部署流程

由于第一次部署&#xff0c;也是第一次自己用 Nginx , 百度了很久&#xff0c;没有看到想看的步骤&#xff0c;所以作此文以记录&#xff0c;也是给像我一样的人一个大概方向。 注&#xff1a;windows系统 1、首先要弄好jar包的运行环境。 安装jdk 详细安装过程引用 jdk的完整…