最近在学习golang源码,学习golang源码是学习golang的十分好的路径。

先来记录一波sync包的学习。版本 go1.14.2 darwin/amd64

sync.WaitGroup

咱们个别应用sync.waitGroup 做并发管制,应用形式个别如下

func main() {    wg := sync.WaitGroup{}    for i := 0; i < 10; i++ {        wg.Add(1)        go func(index int) {            defer wg.Done()            fmt.Println(index)        }(i)    }        wg.Wait()}

所以咱们先看下waitGroup 次要有是哪个函数,Add, Done, Wait 函数,在看函数源码前,咱们先看下WaitGroup 构造

type WaitGroup struct {    noCopy noCopy          //禁止拷贝,如果有拷贝构建不会报错,能够用go vet 或 go tool vet 检测是否有拷贝谬误,    state1 [3]uint32      //重要,存有 计数器,期待数,信号量的值}

其中 state1 成员寄存的值在64 位零碎中如下:

<img src="/Users/guanjingyun/Library/Application Support/typora-user-images/image-20201226143123260.png" alt="image-20201226143123260" style="zoom:33%;" />

接下来看下Add办法

func (wg *WaitGroup) Add(delta int) {  statep, semap := wg.state()   //获取wg状态,statep地址(高32位的计数器值和低32位的期待数量值), semap 信号量    if race.Enabled {  //数据竞态检测,默认是false,开启耗费cpu性能 ,先不论        _ = *statep        if delta < 0 {             race.ReleaseMerge(unsafe.Pointer(wg))        }        race.Disable()        defer race.Enable()    }    state := atomic.AddUint64(statep, uint64(delta)<<32) //原子操作给计数器加上delta的值    v := int32(state >> 32)     //高32位 计数器    w := uint32(state)   //低32位 期待数    if race.Enabled && delta > 0 && v == int32(delta) {  //数据竞态检测,先不论        race.Read(unsafe.Pointer(semap))    }    if v < 0 {                                  //计数器 <0         panic("sync: negative WaitGroup counter")    }    if w != 0 && delta > 0 && v == int32(delta) {    //在add之前,曾经调用过wait函数??(没太看明确)        panic("sync: WaitGroup misuse: Add called concurrently with Wait")    }    if v > 0 || w == 0 { // wait数 为0 或 计数>0 间接返回        return    }    if *statep != state {   // ? 实践上应该相等        panic("sync: WaitGroup misuse: Add called concurrently with Wait")    }        *statep = 0    for ; w != 0; w-- {  //计数器 为0, 开释所有wait 信号量,        runtime_Semrelease(semap, false, 0)    }}

Done函数, 很简略了:

// Done decrements the WaitGroup counter by one.func (wg *WaitGroup) Done() {    wg.Add(-1)}

Wait函数, 把数据竞态检测 局部去了,为代码看起来简洁

func (wg *WaitGroup) Wait() {    statep, semap := wg.state()  //获取wg状态,statep地址(高32位的计数器值和低32位的期待数量值), semap 信号量    for {        state := atomic.LoadUint64(statep)         v := int32(state >> 32)  //如上        w := uint32(state)        if v == 0 {  //计数为0,不必wait            return        }        if atomic.CompareAndSwapUint64(statep, state, state+1) { //期待数加1            runtime_Semacquire(semap)    //获取信号量            if *statep != 0 {                panic("sync: WaitGroup is reused before previous Wait has returned")            }            return        }    }}

runtime_Semacquire 和 runtime_Semrelease 获取和开释信号量,用于休眠和唤醒 协程,具体实现等下次深入研究下