
sync.Cond 为什么不能单独用 sync.Cond?
sync.Cond 不是独立的同步原语,它必须绑定一个已有的互斥锁(<em>sync.Mutex</em> 或 sync.RWMutex)。直接 new 出来就调用 Wait() 会 panic:sync: Cond.Wait with uninitialized mutex。
常见错误现象是程序启动不久就崩溃,堆栈里看到 runtime.throw 和 sync.(*Cond).Wait。这是因为 sync.NewCond(nil) 合法,但后续调用 Wait() 时内部会尝试操作锁的 state 字段,而 nil 指针导致 panic。
使用场景只有一种:你已经在用某个锁保护共享状态,现在想加“等待某条件成立”的能力。比如生产者往缓冲区写数据,消费者要等 len(buf) > 0 才读。
- 必须传入非 nil 的锁指针:
cond := sync.NewCond(&mu),其中mu是已声明的sync.Mutex - 锁类型必须和业务中保护同一块数据的锁完全一致;混用
sync.RWMutex和sync.Mutex会导致未定义行为 -
Cond本身不提供排他性,所有逻辑仍靠外部锁保证
Wait() 为什么要先解锁再挂起?
Wait() 的行为是原子的:它会自动释放关联的锁,然后阻塞当前 goroutine;被唤醒后,又自动重新获取该锁,才返回。这个设计是为了避免死锁 —— 如果不释放锁,其他 goroutine 就无法修改条件、调用 Signal() 或 Broadcast()。
立即学习“go语言免费学习笔记(深入)”;
容易踩的坑是,在 Wait() 返回后,条件不一定成立。因为可能有多个 goroutine 同时被唤醒(尤其是用 Broadcast()),或者被虚假唤醒(spurious wakeup)。所以必须把 Wait() 放在 for 循环里检查条件:
mu.Lock()
for len(data) == 0 {
cond.Wait() // 自动 unlock → 等待 → 自动 lock
}
// 此时 data 非空,可安全消费
value := data[0]
data = data[1:]
mu.Unlock()
- 不用 for 循环而用 if,大概率导致读到空 slice 或 panic
-
Wait()返回时锁一定已被重新持有,不需要手动Lock() - 不要在
Wait()前 unlock,否则 panic:sync: Cond.Wait on uninitialized or copied Cond
Signal() 和 Broadcast() 的唤醒差异很实际
Signal() 最多唤醒一个正在 Wait() 的 goroutine,Broadcast() 唤醒全部。这不是“建议”或“大概率”,而是确定的行为,但具体唤醒哪个由调度器决定,不可预测。
性能影响明显:如果上百个 goroutine 在等,Broadcast() 会一次性让它们全部竞争锁,造成“惊群效应”,CPU 和锁争用陡增。而 Signal() 更轻量,但也意味着你得确保唤醒的是“真正能推进工作的那个”。
使用场景决定选哪个:
- 生产者每次只放一个任务,消费者只要一个能干活就行 → 用
Signal() - 缓冲区清空了,所有等待“非空”的消费者都该重试 → 用
Broadcast() - 条件是“超时”或“取消”,所有等待者都需要退出 → 用
Broadcast()
注意:Signal() 对没有 waiter 的情况完全无害;Broadcast() 也是。两者都不需要判空。
为什么 Cond 很少出现在标准库以外的代码里?
因为绝大多数场景,channel 更简单、更安全、更符合 Go 的风格。比如用 chan struct{} 通知,或用带缓冲的 chan T 直接传递数据,天然避免了锁管理、虚假唤醒、循环检查等细节。
sync.Cond 真正有用的地方很窄:
- 共享状态复杂,无法轻易塞进 channel(比如多个字段耦合的状态机)
- 性能极端敏感,且 profiling 确认 channel 的内存分配或调度开销成了瓶颈
- 需要单个锁保护多种条件(多个
Cond复用一个sync.Mutex)
这时候你才会写:
var mu sync.Mutex var hasData = sync.NewCond(&mu) var isFull = sync.NewCond(&mu)
但一旦这么写,就要格外小心:每个 Wait() 前的条件判断必须精准对应它所属的 Cond,错绑或漏判就会卡死。
真正难的不是怎么调用 Wait(),而是想清楚——这个条件,到底该由谁负责变更、谁负责检查、谁来决定唤醒谁。这些逻辑不在 API 里,而在你的状态流转设计里。










