Go并发原语之Cond, Once, Pool
Cond
- 结构
- l Locker
- nocopy nocopy
- notify notifyList
- 实现
- Wait()
- 先添加到通知列表(调用runtime_notifyListAdd)
- 对locker解锁
- 调用等待接收通知(调用runtime_notifyListWait)
- 内部实现就是会把协程gopark起来,使其进入等待状态
- 收到通知后解除阻塞,继续执行locker加锁操作
- Signal()
- 通知列表中的一个goroutine(runtime_notifyListNotifyOne)
- 内部实现就是最终调用goready唤醒等待列表中的第一个goroutine
- Broadcast()
- 注意点
- 由于Wait方法会先调用Unlock解锁,所以在调用Wait方法前务必先进行加锁操作
- Wait方法唤醒后应注意使用场景,可以使用for或if进行判断。如果唤醒后不满足条件,需要再次调用Wait方法等待,即可以使用for循环如for c.Wait() {}进行等待
- 使用场景
- 类似于达到某个条件后才能继续执行的场景,如生产消费模式,需要生产一个商品后才能获取并消费该商品
Once
- 结构
- done uint32
- metux sync.Metux
- 实现
- 通过 automic.LoadUint32(&done),原子获取done检查是否已经操作,已经做完了就直接返回
- 这里不可以使用cas进行赋值操作,否则如果同时多个goroutine进入时,虽然只有一个会执行,但是其他的goroutine会立即返回。如果刚好要求是需要等待这个函数必须真正执行过的话,其他立即返回的goroutine并不能保证单例函数已经被执行。
- 否则,加锁,继续检查变量done是否已更新,如果已更新则直接返回
- 如果还没有被更新,意味着操作还没执行,调用函数,并保证调用结束后最终会将done的值设置成1
Pool
- 结构
- local unsafe.Pointer
- P数组,GMP的P,[P]poolLocal。这里的描述可能有些简化,实际上local指向的是一个包含多个poolLocal的数组,每个P都有一个对应的poolLocal实例。
- 实现
- Get() any
- 优先找当前p的private属性,有则返回。
- 无则继续找shared队列,有则返回。
- 无则继续去其他P中偷一个,看看偷成功不(偷其他的共享队列中的值),成功则返回。
- 失败则继续找victim缓存,如果找到返回。
- 如果没找到,只能继续进行调用New()函数创建一个。
- victim保证了GC后的一个性能平稳,避免由于GC导致local对象被回收出现的短暂需要重新创建大量对象。victim将会在第二次GC后被垃圾回收,然后local又会重新添加到这里,保证了至少一个地方有缓存。
- Put(any x)
- 检查当前P是否private有值,如果无值,那就放入本地shared共享队列中。
- 如果有值,那就需要放入自己的共享队列中。