sync.cond 必须配合锁使用,它本身无互斥能力,仅是通知调度器;所有共享状态读写必须在锁的临界区内,wait() 前须用 for 循环检查条件以防虚假唤醒,signal/broadcast 必须在锁内完成状态更新后调用。

sync.Cond 必须配合锁使用,不能单独存在
sync.Cond 本身不提供任何互斥能力,它只是个“通知调度器”。你传给 sync.NewCond() 的那个锁(比如 *sync.Mutex)才是保护共享状态的唯一防线。常见错误是只创建 cond 却忘了在所有读写共享变量的地方加锁,结果条件判断和状态修改全在竞态下进行——cond.Wait() 唤醒后看到的可能是过期或撕裂的数据。
必须确保:所有对条件变量所依赖的共享状态(如 ready、len(queue))的读取和修改,都发生在该锁的临界区内。
-
cond.L.Lock()和cond.L.Unlock()要成对出现,且覆盖全部条件检查与状态变更逻辑 - 不要把锁对象和 cond 拆开管理;
cond.L就是那个你传进去的锁,直接用它 - 若用
*sync.RWMutex,注意Wait()内部调用的是Unlock(),所以必须配对R Lock()/RUnlock()不行,得用Lock()/Unlock()
Wait() 前必须用 for 循环检查条件,不能用 if
Go 运行时可能因信号、调度等原因让 cond.Wait() 提前返回(虚假唤醒),此时条件未必成立。如果只写 if !ready { cond.Wait() },一旦被虚假唤醒就会跳过等待直接执行后续逻辑,大概率 panic 或逻辑错乱。
正确结构只有一种:
cond.L.Lock()
for !conditionMet() {
cond.Wait() // 自动 unlock → sleep → wake up → auto re-lock
}
// 此时 conditionMet() 一定为 true,且 cond.L 已 locked
// ... 访问共享资源
cond.L.Unlock()
- 循环体里除了
cond.Wait()不该有其他语句;尤其不能在里面改状态或加日志,会破坏原子性 - 条件判断必须是纯函数式、无副作用的表达式,比如
len(q.items) > 0,而不是调用某个可能改变状态的方法 - 哪怕你 100% 确信不会虚假唤醒,也必须写
for—— 这是 Go sync.Cond 的契约,不是可选项
Signal() 和 Broadcast() 的调用时机必须在锁内修改状态之后
通知不是“喊一嗓子就完事”,而是要确保被唤醒的 goroutine 看到的是最新、一致的状态。典型反例:先 cond.Signal() 再改 ready = true,那被唤醒的 goroutine 进入 for 循环一看还是 false,又掉进 Wait(),造成永久阻塞。
通知方的标准流程是:
cond.L.Lock() // 修改共享状态(如:queue.push(item), ready = true, count++) cond.Signal() // 或 cond.Broadcast() cond.L.Unlock()
-
Signal()只唤醒一个等待者,适合“消费一次即失效”的场景(如单任务分发) -
Broadcast()唤醒全部,适合“状态变更影响所有人”的场景(如配置重载、服务就绪) - 通知操作本身不要求持有锁,但为了状态一致性,必须在锁内完成状态变更 + 通知两步
别用 sync.Cond 替代 channel,除非你真需要广播或复杂条件组合
channel 是 Go 并发的首选通信机制,类型安全、自带同步、语义清晰。sync.Cond 的优势仅在于:能高效地向 *多个* goroutine 广播同一事件,或支持 *非单一数据值* 的复杂条件判断(比如 “缓冲区剩余空间 ≥ X 且 CPU 负载 ≤ Y”)。
以下情况优先选 channel:
- 生产者往队列塞数据,消费者从队列取数据(用带缓冲 channel 更简洁)
- 需要传递具体值(如任务结构体、错误信息)
- 只有一对一或一对少数协程通信
sync.Cond 真正适用的场景很窄:
- 资源池中多个 worker 等待任意空闲 slot(
Broadcast()配合for len(pool) ) - 配置热更新后通知所有监听模块重新加载(状态变更 + 全局广播)
- 多个 goroutine 协同等待复合条件(比如“所有子任务完成”或“超时 + 失败数 ≥ N”)
最容易被忽略的一点:sync.Cond 没有所有权语义,也没办法像 channel 那样关闭来表示“永远不会再有通知”。一旦漏掉某次 Signal() 或逻辑分支没走到通知路径,等待者就卡死——这种死锁比 channel 的死锁更难排查。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











