关键词:
针对Golang 1.9的sync.WaitGroup进行分析,与Golang 1.10基本一样除了将panic
改为了throw
之外其他的都一样。源代码位置:
sync\waitgroup.go
。
结构体
type WaitGroup struct
noCopy noCopy // noCopy可以嵌入到结构中,在第一次使用后不可复制,使用go vet作为检测使用
// 位值:高32位是计数器,低32位是goroution等待计数。
// 64位的原子操作需要64位的对齐,但是32位。编译器不能确保它,所以分配了12个byte对齐的8个byte作为状态。
state1 [12]byte // byte=uint8范围:0~255,只取前8个元素。转为2进制:0000 0000,0000 0000... ...0000 0000
sema uint32 // 信号量,用于唤醒goroution
不知道大家是否和我一样,不论是使用Java的CountDownLatch还是Golang的WaitGroup,都会疑问,可以装下多个线程|协程等待呢?看了源码后可以回答了,可以装下
1111 1111 1111 ... 1111
\________32___________/
2^32个辣么多!所以不需要担心单机情况下会被撑爆了。
函数
以下代码已经去掉了与核心代码无关的race代码。
Add
添加或者减少等待goroutine的数量。
添加的delta,可能是负的,到WaitGroup计数器。
- 如果计数器变为零,所有被阻塞的goroutines都会被释放。
- 如果计数器变成负数,就增加恐慌。
func (wg *WaitGroup) Add(delta int)
// 获取到wg.state1数组中元素组成的二进制对应的十进制的值
statep := wg.state()
// 高32位是计数器
state := atomic.AddUint64(statep, uint64(delta)<<32)
// 获取计数器
v := int32(state >> 32)
w := uint32(state)
// 计数器为负数,报panic
if v < 0
panic("sync: negative WaitGroup counter")
// 添加与等待并发调用,报panic
if w != 0 && delta > 0 && v == int32(delta)
panic("sync: WaitGroup misuse: Add called concurrently with Wait")
// 计数器添加成功
if v > 0 || w == 0
return
// 当等待计数器> 0时,而goroutine设置为0。
// 此时不可能有同时发生的状态突变:
// - 增加不能与等待同时发生,
// - 如果计数器counter == 0,不再增加等待计数器
if *statep != state
panic("sync: WaitGroup misuse: Add called concurrently with Wait")
// Reset waiters count to 0.
*statep = 0
for ; w != 0; w--
// 目的是作为一个简单的wakeup原语,以供同步使用。true为唤醒排在等待队列的第一个goroutine
runtime_Semrelease(&wg.sema, false)
// unsafe.Pointer其实就是类似C的void *,在golang中是用于各种指针相互转换的桥梁。
// uintptr是golang的内置类型,是能存储指针的整型,uintptr的底层类型是int,它和unsafe.Pointer可相互转换。
// uintptr和unsafe.Pointer的区别就是:unsafe.Pointer只是单纯的通用指针类型,用于转换不同类型指针,它不可以参与指针运算;
// 而uintptr是用于指针运算的,GC 不把 uintptr 当指针,也就是说 uintptr 无法持有对象,uintptr类型的目标会被回收。
// state()函数可以获取到wg.state1数组中元素组成的二进制对应的十进制的值
func (wg *WaitGroup) state() *uint64
if uintptr(unsafe.Pointer(&wg.state1))%8 == 0
return (*uint64)(unsafe.Pointer(&wg.state1))
else
return (*uint64)(unsafe.Pointer(&wg.state1[4]))
Done
相当于Add(-1)。
func (wg *WaitGroup) Done()
// 计数器减一
wg.Add(-1)
Wait
执行阻塞,直到所有的WaitGroup数量变成0。
func (wg *WaitGroup) Wait()
// 获取到wg.state1数组中元素组成的二进制对应的十进制的值
statep := wg.state()
// cas算法
for
state := atomic.LoadUint64(statep)
// 高32位是计数器
v := int32(state >> 32)
w := uint32(state)
// 计数器为0,结束等待
if v == 0
// Counter is 0, no need to wait.
return
// 增加等待goroution计数,对低32位加1,不需要移位
if atomic.CompareAndSwapUint64(statep, state, state+1)
// 目的是作为一个简单的sleep原语,以供同步使用
runtime_Semacquire(&wg.sema)
if *statep != 0
panic("sync: WaitGroup is reused before previous Wait has returned")
return
使用注意事项
- WaitGroup不能保证多个 goroutine 执行次序
- WaitGroup无法指定固定的goroutine数目
golangwaitgroup.go(代码片段)
android音频源码分析——audioflinger(代码片段)
Android音频源码分析——AndroidRecord录音(一)Android音频源码分析——AndroidRecord录音(二)Android音频源码分析——AndroidRecord音频数据传输流程Android音频源码分析——audioserver启动Android音频源码分析——AudioFlingerAnd... 查看详情
popupwindow源码分析(代码片段)
...1.2显示PopupWindow1.3最简单的创建1.4注意问题宽和高属性2.源码分析2.1setContentView(ViewcontentView)2.2showAsDropDown()源码2.3dismiss()源码分析2.4PopupDecorView源码分析3.经典总结3.1PopupWindow和Dialog有什么区别? 查看详情
vuex源码分析(代码片段)
VueX源码分析(4)/modulestore.js/module/module.jsimportforEachValuefrom‘../util‘//Basedatastructforstore‘smodule,packagewithsomeattributeandmethodexportdefaultclassModuleconstructor(rawModule,runtime)th 查看详情
android音频源码分析——audiotrack设备选择(代码片段)
Android音频源码分析——AndroidRecord录音(一)Android音频源码分析——AndroidRecord录音(二)Android音频源码分析——AndroidRecord音频数据传输流程Android音频源码分析——audioserver启动Android音频源码分析——AudioFlingerAnd... 查看详情
vuex源码分析(代码片段)
VueX源码分析(2)剩余内容/module/pluginshelpers.jsstore.jshelpers要从底部开始分析比较好。也即先从辅助函数开始再分析那4个map函数mapState。helpers.jsgetModuleByNamespace/***Searchaspecialmodulefromstorebynamespace.ifmodulenotexist,printerrorm 查看详情
arouter源码分析——provider源码分析(代码片段)
Arouter源码分析系列建议从最初开始阅读,全部文章请访问https://github.com/AlexMahao/ARouter在之前分析拦截器时,发现拦截器的基础服务InterceptorService是通过Providers的方式构造实例对象的。于是以下进行Providers的分析。首先看... 查看详情
netflixconductor源码分析--client层源码分析(代码片段)
一、Client层总体介绍在正式介绍Client层源码前,我们先来看一下如何在client端与server端通信,demo代码如下:TaskClienttaskClient=newTaskClient();taskClient.setRootURI("http://localhost:8080/api/");//Pointthist 查看详情
threadlocalmap源码分析(代码片段)
ThreadLocalMap的源码分析分析之前我们来看看ThreadLocalMap有哪些成员变量吧!staticclassEntryextendsWeakReference<ThreadLocal<?>>/**ThevalueassociatedwiththisThreadLocal.*/Objectvalue;Entry(ThreadLocal<?>k,O 查看详情
dialogfragment源码分析(代码片段)
目录介绍1.最简单的使用方法1.1官方建议1.2最简单的使用方法1.3DialogFragment做屏幕适配2.源码分析2.1DialogFragment继承Fragment2.2onCreate(@NullableBundlesavedInstanceState)源码分析2.3setStyle(@DialogStyleintstyle,@StyleResinttheme)2.4onActivit 查看详情
flinkparametertoolfromargs源码分析(代码片段)
一、源码路径java/org/apache/flink/streaming/examples/socket/SocketWindowWordCount.java二、源码/**LicensedtotheApacheSoftwareFoundation(ASF)underone*ormorecontributorlicenseagreements.SeetheNOTICEfile*distribute 查看详情
vscode源码分析加载第一个画面(代码片段)
第一篇: vscode源码分析【一】从源码运行vscode第二篇:vscode源码分析【二】程序的启动逻辑,第一个窗口是如何创建的第三篇:vscode源码分析【三】程序的启动逻辑,性能问题的追踪第四篇:vscode源码分析【四】程序启动的... 查看详情
reactnative源码分析——nativeview创建流程(代码片段)
1、ReactNative源码分析(一)——启动流程2、ReactNative源码分析(二)——通信机制3、ReactNative源码分析(三)——NativeView创建流程4、ReactNative源码分析(四)——任务调度5、ReactNative源码分析(... 查看详情
reactnative源码分析——nativeview创建流程(代码片段)
1、ReactNative源码分析(一)——启动流程2、ReactNative源码分析(二)——通信机制3、ReactNative源码分析(三)——NativeView创建流程4、ReactNative源码分析(四)——任务调度5、ReactNative源码分析(... 查看详情
locksupport源码分析(代码片段)
目录LockSupport源码分析LockSupport的实现1.内部重要的属性:2.getBlocker(Thread)与setBlocker(Threadt,Objectarg)源码3.park的其他几个方法4.park()/unpark()与wait()/notify()区别:LockSupport源码分析LockSupport是Java6引入的一个工具类,用于挂起和唤醒线程;LockS... 查看详情
androidsparsearray源码分析(代码片段)
文章目录SparseArray源码分析总结基本使用源码分析基本属性get()put()delete()gc()其他ContainerHelpers类GrowingArrayUtils类SparseArray源码分析总结在Android中,某些场景推荐使用SparseArray替代HashMap,其原因是SparseArray更加节省内存,... 查看详情
jdk源码分析通过源码分析cyclicbarrier(代码片段)
...要在中午聚个会,几个朋友全部到齐后才开始喝酒吃菜。源码CyclicBarrier属性和构造器publicclassCyclicBarrier//互斥锁privatefinalReentrantLocklock=newReentrantLock();//条件等待privatef 查看详情
spring源码分析之abstractapplicationcontext源码分析(代码片段)
首先我觉得分析ApplicationContext必须从它的实现类开始进行分析,AbstractApplicationContext我觉得是一个不错的选择,那我们就从这里开始逐一分析吧,首先我自己手画了一张图,作为索引吧,其中蓝色的为类,紫色的为接口,箭... 查看详情