【Go】sync.WaitGroup 源码分析

WaitGroup

sync.WaitGroup 用于等待一组 goroutine 返回,如:

var wg = sync.WaitGroup{}func do() {time.Sleep(time.Second)fmt.Println("done")wg.Done()
}func main() {go do()go do()wg.Add(2)wg.Wait()fmt.Println("main done")
}

概览

如上面的例子, WaitGroup 只堆外暴露了三个方法:

// 等待的 goroutine 数加 delta
func (wg *WaitGroup) Add(delta int) 
// 等待的 goroutine 数减一
func (wg *WaitGroup) Done() 
// 阻塞,等待这一组 goroutine 全部退出
func (wg *WaitGroup) Wait()
type WaitGroup struct {noCopy noCopystate1 [3]uint32
}

WaitGroup 结构体中也只有两个字段:

  • noCopy: 用来保证不会被开发者错误拷贝
  • state1: 用来保存相关状态量

另外,他还提供了一个私有的方法用来获取状态和信号量

func (wg *WaitGroup) state() (statep *uint64, semap *uint32) {if uintptr(unsafe.Pointer(&wg.state1))%8 == 0 {return (*uint64)(unsafe.Pointer(&wg.state1)), &wg.state1[2]} else {return (*uint64)(unsafe.Pointer(&wg.state1[1])), &wg.state1[0]}
}

statep 就是状态量,注意这里通过 unsafe 将 3 位数组(共 96 位)强转成了 uint64 这会导致部分数据丢失,具体来说,在64位的机器上会丢失最低 32 位,也即 state1[2] 在 32 位机器上会丢失最高 32 位,也即 state1[0], 这也是 64 位和 32 位机器上数组三位元素表示意义不同的原因。

强转之后,以 64 位机器为例,数组第二位会作为 statep 的高 32 位,第一位会作为 statep 的低 32 位,也就是说,此时 statep 的结构如下:

+----------------------+-----------------------+
|                      |                       |
|      Counter         |       Waiter          |
|                      |                       |
+----------------------+-----------------------+

Add

func (wg *WaitGroup) Done() {wg.Add(-1)
}

Done 其实就是对 Add 的一个封装。

func (wg *WaitGroup) Add(delta int) {statep, semap := wg.state()// 把 delta 加到 count 中state := atomic.AddUint64(statep, uint64(delta)<<32)// 获取 countv := int32(state >> 32)// 丢失高 32 位的 Counter, 得到 Waiterw := uint32(state)if v < 0 {panic("sync: negative WaitGroup counter")}// Waiter 不等于 0 说明现在还有 goroutine 没有 done, 这时是不允许 Add 的// 也即在 Wait 的过程中不允许通过 Add 添加 if w != 0 && delta > 0 && v == int32(delta) {panic("sync: WaitGroup misuse: Add called concurrently with Wait")}// 正常修改 Counter 后返回if v > 0 || w == 0 {return}// 到这说明 Counter == 0 并且 delta 不是一个正数(执行 Done,并且是最后一次 Done)// 状态改变,说明有人在 Wait 过程中 Add 了if *statep != state {panic("sync: WaitGroup misuse: Add called concurrently with Wait")}// 状态置 0*statep = 0// 唤醒 Wait 中的 goroutinefor ; w != 0; w-- {runtime_Semrelease(semap, false, 0)}
}

总结一下,首先 Done 只是对 Add 的简单封装,在 Add 时,通过巧妙利用精度丢失和位移运算分别计算出 add 后的 Counter 和 Waiter, 前者表示已经 add 了多少 Goroutine, 后者表示还有多少个 goroutine 需要 Wait, 这里需要注意,在 Wait 的过程中是不允许 Add 新 goroutine 的;在执行 Done 时,只是简单的将 Counter 减 1,直到 Counter == 1 时,也即最后一个 goroutine 已经执行完毕时,Done 会通知 Wait 停止阻塞,并将标志清空。

Wait

func (wg *WaitGroup) Wait() {statep, semap := wg.state()for {state := atomic.LoadUint64(statep)v := int32(state >> 32)// Counter == 0, 没有 Add, 直接返回if v == 0 {return}// 每一次 CAS 让 Waiter 加一,并进入阻塞,等待最后一个 Done 的 goroutine 将其唤醒if atomic.CompareAndSwapUint64(statep, state, state+1) {runtime_Semacquire(semap)if *statep != 0 {panic("sync: WaitGroup is reused before previous Wait has returned")}return}// 如果 CAS 比较没通过,说明在此过程中有 goroutine Done 了,需要重新去获取最新的状态}
}

总结

WaitGroup 用于阻塞某个 Goroutine 以等待一组 goroutine 返回,在实现上,它采用一个长度为 3 的 32 位无符号整型数组保存 Waiter, Counter, 和信号量,每次 Add 时,会将 Counder 加上 delta,而当执行 Done 或 delta 为负数时,如果 Done 的是最后一个 Goroutine, Add 会去唤醒 Wait

执行 Wait 只是将 Waiter 加一并阻塞等待 Add 的唤醒,所以其实 Waiter 的值只会是 0 或 1.

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

什么是响应式设计?为什么要做响应式设计?响应式设计的基本原理是什么?...

页面的设计和开发应当根据用户行为以及设备环境&#xff08;系统平台、屏幕尺寸、屏幕定向等&#xff09;进行相应的响应和调整。具体的实践方式由多方面组成&#xff0c;包括弹性网格和布局、图片、css media query的使用等。无论用户正在使用笔记本还是iPad&#xff0c;我们的…

三个数相减的平方公式_快收好这份小学数学公式大全!孩子遇到数学难题时肯定用得上...

必背定义、定理公式1.三角形的面积&#xff1d;底高2 公式 S&#xff1d; ah22.正方形的面积&#xff1d;边长边长公式 S&#xff1d; aa3.长方形的面积&#xff1d;长宽公式 S&#xff1d; ab4.平行四边形的面积&#xff1d;底高公式 S&#xff1d; ah5.梯形的面积&#xff1d…

Eclipse 控制console

http://blog.csdn.net/leidengyan/article/details/5686691

【Go】sync.RWMutex源码分析

RWMutex 读写锁相较于互斥锁有更低的粒度&#xff0c;它允许并发读&#xff0c;因此在读操作明显多于写操作的场景下能减少锁竞争的次数&#xff0c;提高程序效率。 type RWMutex struct {w Mutex // held if there are pending writerswriterSem uint32 // sem…

add.attribute向前端传_前端知识-概念篇

1、一次完整的HTTP事务是怎样的一个过程&#xff1f;基本流程&#xff1a;a. 域名解析b. 发起TCP的3次握手c. 建立TCP连接后发起http请求d. 服务器端响应http请求&#xff0c;浏览器得到html代码e. 浏览器解析html代码&#xff0c;并请求html代码中的资源f. 浏览器对页面进行渲…

【数据库】一篇文章搞懂数据库隔离级别那些事(LBCC,MVCC)

MySQL 事务 文章比较长&#xff0c;建议分段阅读 后续如果有改动会在 Junebao.top 之前对事务的了解仅限于知道要么全部执行&#xff0c;要么全部不执行&#xff0c;能背出 ACID 和隔离级别&#xff0c;知其然但不知其所以然&#xff0c;现在觉得非常有必要系统学一下&#xff…

AFNetworking网络请求与图片上传工具(POST)

AFNetworking网络请求与图片上传工具&#xff08;POST&#xff09; .h文件 #import <Foundation/Foundation.h>/** 成功Block */ typedef void(^SuccessBlockType) (id responsData); /** 失败Block */ typedef void(^FaileBlockType) (NSError *error);interface NetD…

api商品分享源码_SSM框架高并发和商品秒杀项目高并发秒杀API源码免费分享

前言&#xff1a;一个整合SSM框架的高并发和商品秒杀项目,学习目前较流行的Java框架组合实现高并发秒杀API源码获取&#xff1a;关注头条号转发文章之后私信【秒杀】查看源码获取方式&#xff01;项目的来源项目的来源于国内IT公开课平台,质量没的说,很适合学习一些技术的基础,…

Golang 定时任务 github/robfig/cron/v3 使用与源码解析

Cron 源码阅读 robfig/cron/v3 是一个 Golang 的定时任务库&#xff0c;支持 cron 表达式。Cron 的源码真实教科书级别的存在&#xff08;可能是我菜 …&#xff09;,真的把低耦合高内聚体现地淋漓尽致&#xff0c;另外其中涉及的装饰器模式&#xff0c;并发处理等都很值得学习…

修改 cmd 字体为 Consolas

windows 下的 cmd 窗口默认的字体有点难看&#xff0c;长时间使用操作 node.js 有点小疲劳&#xff0c;可以修改注册表替换字体为 Consolas&#xff0c;并且可以全屏 cmd 窗口&#xff0c;代码如下&#xff1a; Windows Registry Editor Version 5.00 [HKEY_CURRENT_USER\Conso…

mac下安装前端模板引擎Jinja2

在mac本上安装Jinja2&#xff0c;搜索网上介绍的经验&#xff0c;都是说使用easy_install或者pip安装&#xff0c;比如 #sudo easy_install Jinja2 #sudo pip install Jinja2 也有直接使用 #easy_install Jinja2的&#xff0c;但是我使用上述命令安装总是不成功&#xff0c;提示…

为什么要用python不用origin_Python告诉你为什么百度已死

Python3爬虫百度一下&#xff0c;坑死你&#xff1f;一、写在前面这个标题是借用的路人甲大佬的一篇文章的标题(百度一下&#xff0c;坑死你)&#xff0c;而且这次的爬虫也是看了这篇文章后才写出来的&#xff0c;感兴趣的可以先看下这篇文章。前段时间有篇文章《搜索引擎百度已…

关于 HTTP 的一切(HTTP/1.1,HTTP/2,HTTP/3,HTTPS, CORS, 缓存 ,无状态)

HTTP 为什么会出现 HTTP 协议&#xff0c;从 HTTP1.0 到 HTTP3 经历了什么&#xff1f;HTTPS 又是怎么回事&#xff1f; HTTP 是一种用于获取类似于 HTML 这样的资源的 应用层通信协议&#xff0c; 他是万维网的基础&#xff0c;是一种 CS 架构的协议&#xff0c;通常来说&…

AS 2.0新功能 Instant Run

Instant Run上手作为一个Android开发者&#xff0c;很多的时候我们需要花大量的时间在bulid&#xff0c;运行到真机&#xff08;虚拟机&#xff09;上&#xff0c;对于ios上的Playground羡慕不已&#xff0c;这种情况将在Android Studio 2.0有了很大改善&#xff0c;使用instan…

爬虫cookie过期_python instagram 爬虫

叶湘伦&#xff1a;【文字篇】如何系统地自学 Python&#xff1f;​zhuanlan.zhihu.com直接介绍一下具体的步骤以及注意点&#xff1a;instagram 爬虫注意点instagram 的首页数据是 服务端渲染的&#xff0c;所以首页出现的 11 或 12 条数据是以 html 中的一个 json 结构存在的…

php 无限循环

<?php header("Content-type:text/html;charsetutf-8"); $arr array( array(1, 0, 语文), array(2, 1, 数学), array(3, 0, 英文), array(4, 3, 美术), ); function xunhuan($pid 0) { global $arr; foreach ($arr as $value) { if ($value[1] $pid) { ech…

MySQL InnoDB 是如何存储数据的

InnoDB 是怎么存储数据的 本文是《MySQL 是怎样运行的 —— 从根儿上理解 MySQL》读书总结&#xff0c;强烈推荐这本书&#xff1b; CSDN 不能显示 SVG&#xff0c;可能有图片加载不出来&#xff0c;可以到 我的博客 上看。 数据目录 众所周之&#xff0c;MySQL 的数据是存储在…

蔬菜大棚成本_蔬菜大棚种植成本和利润究竟如何?种植户有话说

大棚蔬菜种植&#xff0c;到底利润高不高&#xff0c;就让亲身体验过的人来说下自己的情况吧。农大老家山东也是大棚蔬菜种植比较早的地方&#xff0c;直到现在大棚种植蔬菜在各地都还是不少。大棚蔬菜种植&#xff0c;是有相应的补贴政策&#xff0c;在农业种植当中&#xff0…

WebSocket实战之————GatewayWorker使用笔记例子

参考文档&#xff1a;http://www.workerman.net/gatewaydoc/ 目录结构 ├── Applications // 这里是所有开发者应用项目 │ └── YourApp // 其中一个项目目录&#xff0c;目录名可以自定义 │ ├── Events.php // 开发者只需要关注这个文件 │ ├── st…

[转]关于凸优化的一些简单概念

没有系统学过数学优化&#xff0c;但是机器学习中又常用到这些工具和技巧&#xff0c;机器学习中最常见的优化当属凸优化了&#xff0c;这些可以参考Ng的教学资料&#xff1a;http://cs229.stanford.edu/section/cs229-cvxopt.pdf&#xff0c;从中我们可以大致了解到一些凸优化…