cond.Wait()必须配合for循环使用,因为可能被虚假唤醒,需循环检查条件是否真正满足;cond.Signal()唤醒一个等待goroutine,cond.Broadcast()唤醒所有;cond.L必须是sync.Mutex或sync.RWMutex指针且全程唯一。

cond.Wait() 为什么必须配合 for 循环使用?
因为 cond.Wait() 可能被虚假唤醒(spurious wakeup),即没有收到 cond.Signal() 或 cond.Broadcast() 就返回。不加循环判断条件,线程可能在条件未满足时继续执行,导致逻辑错误。
典型写法是:
cond.L.Lock()
defer cond.L.Unlock()
for !conditionMet() {
cond.Wait()
}
// 此时 conditionMet() 为 true,安全执行后续逻辑
- 条件检查必须在锁内进行,且放在
cond.Wait()前后都需持有锁 -
conditionMet()应该是可快速计算的布尔表达式,比如检查某个全局变量、channel 是否有值、切片是否非空等 - 不要用
if替代for—— 即使只期望唤醒一次,也不能跳过循环校验
cond.Broadcast() 和 cond.Signal() 的实际区别在哪?
cond.Signal() 唤醒**一个**正在等待的 goroutine(具体哪个由调度器决定);cond.Broadcast() 唤醒**所有**当前阻塞在 cond.Wait() 上的 goroutine。
选哪个取决于业务语义:
立即学习“go语言免费学习笔记(深入)”;
- 用
cond.Signal():当只有一个 goroutine 能“消费”本次状态变更时,比如生产者往缓冲队列 push 一项,只需唤醒一个消费者 - 用
cond.Broadcast():当状态变更影响所有等待者,比如关闭信号、重置配置、资源池整体可用,必须让全部 goroutine 重新检查条件 - 误用
Signal()替代Broadcast()是常见死锁原因:多个 goroutine 等待同一条件,但只唤醒一个,其余永久阻塞
为什么 cond.L 必须是 *sync.Mutex 或 *sync.RWMutex?
sync.Cond 不维护自己的锁,它只是“借用”你传入的锁对象来协调等待和唤醒。这个锁必须满足两个硬性要求:
- 类型必须是指针:
*sync.Mutex或*sync.RWMutex,传值会复制锁,导致 Wait/Signal 失效 - 所有对共享数据的读写、以及所有
cond.Wait()/cond.Signal()/cond.Broadcast()调用,**必须使用同一个锁实例** - 不能混用
sync.Mutex和sync.RWMutex:即使都是指针,类型不匹配会导致 panic 或未定义行为
错误示例:cond := sync.NewCond(&sync.Mutex{}) —— 这里传的是 &sync.Mutex{} 的副本地址,但更危险的是后续锁操作用了另一个 mutex 实例。
Wait 之后锁何时被释放、何时被重新获取?
cond.Wait() 的行为是原子的:它先**自动释放**你持有的锁,然后挂起 goroutine;被唤醒后,它会**自动重新获取**同一把锁,再返回。
- 所以调用
cond.Wait()前必须已持有cond.L,否则 panic - 返回时锁一定已被重新锁定,无需手动
Lock() - 这意味着:从
cond.Wait()返回后,你可以立即安全访问受该锁保护的共享状态 - 但要注意——两次
cond.Wait()之间,其他 goroutine 可能已修改状态,所以必须用 for 循环再次校验
这个“释放→等待→重锁”的过程是 sync.Cond 正确性的核心机制,跳过它或手动干预锁状态,几乎必然引发竞态或死锁。


















