golang源代码阅读,sync系列-WaitGroup

概览

使用场景:管理多个goroutine,用于等待多个goroutine都完成后,主goroutine继续运行

例子

wg  := new(sync.WaitGroup)
wg.Add(n) //n为要等待的goroutine个数
go func() {
    wg.Done() //对应wg.Add(-1)
}
...
wg.Wait()

源码解读

总结

通过state1一个数组来表示当前的delta,waiter、semap信号量大小,如果调用Add,会delta会加上输入的大小,然后返回,如果多个Wait调用,每个Wait都会阻塞,每个进入waiter都会加1,因为semap信号量为0,所以每个waiter都会阻塞,直到delta为0(关键点,Add加负数的时候,如果delta不为0,直接返回,为0,则循环释放信号量),add会循环waiter,每回减1,释放信号量,每回减1,直到为0,每释放一个信号量,一个Wait返回,直到信号量为0,每个Wait都返回了。

结构体字段介绍

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 do not ensure it. So we allocate 12 bytes and then use
	// the aligned 8 bytes in them as state, and the other 4 as storage
	// for the sema.
	state1 [3]uint32
}
  1. noCopy 标记位,用作go vet检查,如果当前Struct被copy了,则会报错
    实例代码:
	wg := sync.WaitGroup{}
	wg.Add(1)

	copyOne := wg
	copyOne.Add(1)
	go func() {
		fmt.Println("add -1")
		wg.Done()
	}()
	wg.Wait()
}

上面代码可以正常运行
goland会提示如下错误
在这里插入图片描述
运行go vet会报下面错误
在这里插入图片描述
2. state1存储信号量、waiter、delta
state1[0]:semap,信号量
state1[1]:waiter个数
state1[2]:delta
在这里插入图片描述

函数介绍

Add函数

func (wg *WaitGroup) Add(delta int) {
	statep, semap := wg.state() //对state1进行拆分,获取statep和semap,statep对应state1[1]state1[2],semap的对应statep[0]
	state := atomic.AddUint64(statep, uint64(delta)<<32) //stetep[1]即state1[2]存储delta
	v := int32(state >> 32) //v为state[1],当前delta大小
	w := uint32(state) // w为state[0],当前waiter个数

	if v < 0 {
		panic("sync: negative WaitGroup counter")
	}
	if w != 0 && delta > 0 && v == int32(delta) {
		panic("sync: WaitGroup misuse: Add called concurrently with Wait")
	}
	if v > 0 || w == 0 { //关键点,当前还未调用Wait()或者当前的delta总数还是大于0
		return
	}
	
	if *statep != state {
		panic("sync: WaitGroup misuse: Add called concurrently with Wait")
	}
	// Reset waiters count to 0.
	// 当前v即delta为0时,则一个个通知waiter,通过进行release信号量,实际即semap+1
	*statep = 0
	for ; w != 0; w-- { //如果有多个waiter,semap为waiter个数
		runtime_Semrelease(semap, false, 0)
	}
}

Wait函数

func (wg *WaitGroup) Wait() {
	statep, semap := wg.state() //同Add方法第一行,获取statep和semap
	for {
		state := atomic.LoadUint64(statep) 
		v := int32(state >> 32) // 当前delta大小
		w := uint32(state) //当前waiter个数
		if v == 0 { // 如果当前delta大小为0,则直接返回,没有Add过,或者已经被Done掉了
			return
		}
		// Increment waiters count. 如果当前state没有被修改过,则statep加1,实际是w+1了,即多了一个waiter
		if atomic.CompareAndSwapUint64(statep, state, state+1) {
			runtime_Semacquire(semap) //这是acquire一个实际为semap减1,因为semap当前还是0,这时会被阻塞
			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

实际为Add -1

func (wg *WaitGroup) Done() {
	wg.Add(-1)
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值