结论:sync.Cond 是条件变量——让 goroutine 在某个条件不满足时挂起等待(Wait),条件可能满足时被 Signal(唤醒一个)或 Broadcast(唤醒全部)通知后重新检查条件。它内嵌一个 Locker,Wait 的原子语义是"释放锁并挂起,被唤醒后重新获得锁"。当等待条件涉及共享状态且需要广播语义时,它比 channel 合适。
展开:标准用法模式(循环检查是铁律):
c := sync.NewCond(&sync.Mutex{})
// 等待方
c.L.Lock()
for !ready() { // 必须 for 而非 if:应对虚假/抢跑唤醒
c.Wait()
}
c.L.Unlock()
// 通知方:修改状态后
c.L.Lock(); ready = true; c.L.Unlock()
c.Broadcast()
为什么有时必须用它:1)channel 是"发一个收一个",没有广播——一对多通知得 close channel 这种一次性技巧,无法重复使用;Cond 可反复 Signal/Broadcast。2)等待条件是基于共享状态的谓词(队列非空、计数达标),channel 表达的是事件而非状态谓词。3)与锁天然一体,保证"检查条件 + 等待"的原子性。典型场景:有界队列、读写者问题、等多个任务就绪的 barrier。易错点:1)Wait 前必须持锁、条件检查必须用 for 循环(Signal 后锁被竞争者抢走,条件可能再次不成立);2)通知方改状态也要持锁,否则丢通知;3)Cond 不允许拷贝。实际工程里 channel 能解决 80% 的场景,Cond 留给"状态谓词 + 广播"这类 channel 表达别扭的问题。
追问方向:条件变量的虚假唤醒(spurious wakeup)在 Go 中的成因(允许实现层面提前返回)、K8s informer 的 DeltaFIFO 为何用 Cond 而非 channel。