条件变量 Cond
Go 标准库提供 Cond 原语的目的是,为等待 / 通知场景下的并发问题提供支持。Cond 通
常应用于等待某个条件的一组 goroutine,等条件变为 true 的时候,其中一个 goroutine
或者所有的 goroutine 都会被唤醒执行。
顾名思义,Cond 是和某个条件相关,这个条件需要一组 goroutine 协作共同完成,在条
件还没有满足的时候,所有等待这个条件的 goroutine 都会被阻塞住,只有这一组
goroutine 通过协作达到了这个条件,等待的 goroutine 才可能继续进行下去。
那这里等待的条件是什么呢?等待的条件,可以是某个变量达到了某个阈值或者某个时间
点,也可以是一组变量分别都达到了某个阈值,还可以是某个对象的状态满足了特定的条
件。总结来讲,等待的条件是一种可以用来计算结果是 true 还是 false 的条件
使用cond
func NeWCond(l Locker) *Cond
func (c *Cond) Broadcast()
func (c *Cond) Signal()
func (c *Cond) Wait()
首先,Cond 关联的 Locker 实例可以通过 c.L 访问,它内部维护着一个先入先出的等待队
列。
Signal 方法,允许调用者 Caller 唤醒一个等待此 Cond 的 goroutine。如果此时没有等
待的 goroutine,显然无需通知 waiter;如果 Cond 等待队列中有一个或者多个等待的
goroutine,则需要从等待队列中移除第一个 goroutine 并把它唤醒
调用 Signal 方法时,不强求你一定要持有 c.L 的锁。
Broadcast 方法,允许调用者 Caller 唤醒所有等待此 Cond 的 goroutine。如果此时没有
等待的 goroutine,显然无需通知 waiter;如果 Cond 等待队列中有一个或者多个等待的
goroutine,则清空所有等待的 goroutine,并全部唤醒
调用 Broadcast 方法时,不强求你一定持有 c.L 的锁。
Wait 方法,会把调用者 Caller 放入 Cond 的等待队列中并阻塞,直到被 Signal 或者
Broadcast 的方法从等待队列中移除并唤醒。
调用 Wait 方法时必须要持有 c.L 的锁。
Cond结构体
标准库中的 Cond 并发原语初始化的时候,需要关联一个 Locker 接口的实例,一般我们
使用 Mutex 或者 RWMutex。
type Cond struct {
// 可以将 noCopy 添加到第一次使用后不得复制的结构中
noCopy noCopy
// L is held while observing or changing the condition
// 当观察或者修改等待条件的时候需要加锁
L Locker
// 等待队列
notify notifyList
// copyChecker 是一个辅助结构,可以在运行时检查 Cond 是否被复制使用
checker copyChecker
}
NewCond
// NewCond returns a new Cond with Locker l.
func NewCond(l Locker) *Cond {
return &Cond{L: l}
}
Wait
Wait 把调用者加入到等待队列时会释放锁,在被唤醒之后还会请求锁。在阻塞休眠期间,
调用者是不持有锁的,这样能让其他 goroutine 有机会检查或者更新等待变量。
func (c *Cond) Wait() {
c.checker.check()
t := runtime_notifyListAdd(&c.notify)
c.L.Unlock()
runtime_notifyListWait(&c.notify, t)
c.L.Lock()
}
cond.Wait 方法的实现是,把当前调用者加入到 notify 队列之中后会释放锁(如果不释放锁,其他 Wait 的调用者就没有机会加入到 notify 队列中了),然后一直等待;等调用者被唤醒之后,又会去争抢这把锁。如果调用 Wait 之前不加锁的话,就有可能 Unlock 一个未加锁的 Locker。所以切记,调用 cond.Wait 方法之前一定要加锁。
Signal
func (c *Cond) Signal() {
c.checker.check()
runtime_notifyListNotifyOne(&c.notify)
}
Broadcast
func (c *Cond) Broadcast() {
c.checker.check()
runtime_notifyListNotifyAll(&c.notify)
}