- 参考
- [好文](https://draveness.me/golang/docs/part3-runtime/ch06-concurrency/golang-sync-primitives/)
- 经典用法
https://gobyexample.com/waitgroups- 构造wg
- wg.Add(1)
- defer wg.Done(),也就是wg.Add(-1)
- 并行搞起
- wg.wait()
- WaitGroup重要变量:counter,waiter,sema(信号量)
- 推论:counter==0认为是并发任务执行完毕。
- 为什么?整个流程上来就是Add把counter+1,正常流程会把Add全部执行完再wait。任何情况下,只要wait发现count==0,那就是并行任务执行完了。
- 推论:counter==0认为是并发任务执行完毕。
- Add(delta)
- CAS counter + delta
- counter > 0 或者 waiter == 0 return(为啥这么写?return是为了不执行后面的sema操作(通知等待的goroutine)。counter>0或者waiter==0不执行是为啥?waiter==0都没人在等,当然不需要执行后面的通知goroutine,counter>0说明还没干完活呢没把counter减到0呢,也没必要去执行后面的通知)
- 其实可以看后面的注释:当前的goroutine,当waiters>0的时候把counter设置成了0。(counter==0任务执行完了且有人在等待咱们唤醒,那就要唤醒)(没有return的情况下,说明v==0 && w !=0)。说明了啥呢?现在不可能有并发改state了。为啥?
- Adds不能跟Wait并发发生(看经典用法,按常理,肯定是一个一个add完最后才wait的)
- Wait不会增加waiter如果它看到counter==0
- 其实可以看后面的注释:当前的goroutine,当waiters>0的时候把counter设置成了0。(counter==0任务执行完了且有人在等待咱们唤醒,那就要唤醒)(没有return的情况下,说明v==0 && w !=0)。说明了啥呢?现在不可能有并发改state了。为啥?
- 重置waiter数量为0
- 对sema执行waiter次 runtime_SemRelease
- Wait()
- 死循环
- 获取一遍counter和waiter
- counter == 0,return。(counter归零了==执行完了,可以直接返回,否则说明还没执行完,需要睡一会等人来叫醒我)
- if CAS waiter+1 成功
- 对sema执行1次runtime_Semacquire(睡吧等人叫醒我)
- 返回
- 死循环
- 这过程中又涉及俩底层玩意儿
- runtime_SemRelease(叫醒人)
原子+1,然后通知等待的goroutine(如果一个goroutine在获取过程中阻塞了)
- runtime_Semacquire(睡吧等人叫醒我)
阻塞等待直到信号量>0然后原子-1
- runtime_SemRelease(叫醒人)
- 所以把整个官方例子串起来那就是(假设add两遍)
- wg.Add(1)
- counter=1,waiter=0,返回,sema没改=0
- go funcA() {并行干点A事情; }
- wg.Add(1)
- counter=2,waiter=0,返回,sema没改=0
- go funcB() {并行干点B事情; }
- wg.wait()
- CAS waiter + 1 = 1
- runtime_Semacquire,阻塞等待。
- A干完了wg.Add(-1)
- counter-1=1,counter>0 return
- B干完了wg.Add(-1)
- counter-1=0,waiter=1,没有直接返回
- 重置waiter=0
- 执行1次runtime_SemRelease。
- 原子sema+1=1,通知等待的goroutine(在wait的那哥们)
- wg.wait()收到通知,信号量>0了,原子sema-1=0
- wg.Add(1)
- 更底层怎么实现的?/usr/local/go/src/runtime/sema.go
- runtime_SemRelease(叫醒人)
原子+1,然后通知等待的goroutine(如果一个goroutine在获取过程中阻塞了)
- runtime_Semacquire(睡吧等人叫醒我)【其实这是很多锁的统一套路。比如Java。没啥可说的,这就是沉淀下来的经验,不好深究每一行是为什么】https://blog.csdn.net/waltonhuang/article/details/110797972
阻塞等待直到信号量>0然后原子-1- easycase:直接看一下sema,如果==0,获取失败,否则,原子-1成功则获取成功。
- hardercase:
- 死循环:
- 增加waiter数量
- 再试一下获取,成功就返回
- 排队
- 休眠
- 死循环:
- runtime_SemRelease(叫醒人)
go: WaitGroup 原理: 直到最底层
最新推荐文章于 2024-08-28 16:46:52 发布