Go 标准库源码分析 - sync 的 WaitGroup

WaitGroup常用于多个goroutine协作,主要功能是阻塞等待一组goroutine完成。 

一、数据结构

type WaitGroup struct {
	noCopy noCopy

	state1 [3]uint32    // 用于存放任务计数器、等待者计数器和信号量 
}

WaitGroup采用64位的值来保存计数器,其中高32位为任务计数器,低32位为等待者计数器,另外用32位的值保存信号量。

WaitGroup在使用时需要64位的计数器进行原子操作,这要求计数器的地址是64位对齐的。如果是64位的操作系统,可直接取数组前两位元素作为计数器;如果是32位操作系统,state1可能不是64位对齐的地址,则将数组第一个元素作为信号量而后两个元素为计数器。

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]
	}
}

 

二、使用方法

1. Add

func (wg *WaitGroup) Add(delta int) {
    // 获取计数器和信号量的地址
	statep, semap := wg.state()
    // 增加任务数,为原子操作
	state := atomic.AddUint64(statep, uint64(delta)<<32)
    // 任务计数器
	v := int32(state >> 32)
    // 等待者计数器
	w := uint32(state)
    // 任务数不能为负
	if v < 0 {
		panic("sync: negative WaitGroup counter")
	}
    // 在Add前执行Wait
	if w != 0 && delta > 0 && v == int32(delta) {
		panic("sync: WaitGroup misuse: Add called concurrently with Wait")
	}
	if v > 0 || w == 0 {
		return
	}
	// 执行至此,说明任务数为0,且等待者数不为0
    // 原计数器与现在的不同,可能是Add和Wait同时执行
	if *statep != state {
		panic("sync: WaitGroup misuse: Add called concurrently with Wait")
	}
	// 将计数器归零
	*statep = 0
    // 唤醒等待者
	for ; w != 0; w-- {
		runtime_Semrelease(semap, false, 0)
	}
}

2. Done

func (wg *WaitGroup) Done() {
    // 执行Add方法将任务数减1
	wg.Add(-1)
}

3. Wait

func (wg *WaitGroup) Wait() {
    // 获取计数器和信号量地址
	statep, semap := wg.state()
	for {
        // 原子读计数器的值
		state := atomic.LoadUint64(statep)
        // 任务计数器
		v := int32(state >> 32)
        // 等待计数器
		w := uint32(state)
		if v == 0 {
			// 任务数为0,直接返回
			return
		}
		// 任务数不为0时,原子增加等待者数
		if atomic.CompareAndSwapUint64(statep, state, state+1) {
            // 挂起当前goroutine
			runtime_Semacquire(semap)
            // 在Add方法里,唤醒等待者前会将计数器归零,因此若此处的statep不为0,则说明又调用了Add或Wait方法,导致panic
			if *statep != 0 {
				panic("sync: WaitGroup is reused before previous Wait has returned")
			}
			return
		}
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值