sync.Cond 用于等待他人通知后重新检查条件,必须在 for 循环中调用 Wait() 并配合已初始化的互斥锁,Signal/Broadcast 仅唤醒 goroutine 而不保证条件成立。

sync.Cond 不是用来“等条件成立”的,而是用来“等别人改完共享状态后通知你,你再自己检查一遍”的。 它本身不保存、不判断、不追踪任何条件,单独用 cond.Wait() 没有意义,也一定会 panic。
Wait 必须在 for 循环里,不能用 if
被 Signal() 或 Broadcast() 唤醒后,cond.Wait() 返回时条件**不一定成立**——可能是虚假唤醒,也可能是其他 goroutine 抢先修改了状态。跳过重检就会读空 slice、取错值、panic。
- 错误写法:
if len(queue) == 0 { cond.Wait() }→ 唤醒后直接消费,queue可能还是空 - 正确写法:
for len(queue) == 0 { cond.Wait() }→ 每次返回都重新判断 - 注意:循环体里不要手动
Unlock(),Wait()内部已处理释放与重锁
Cond 必须绑定一个已初始化的锁,且所有状态访问都要走它
sync.NewCond(nil) 不报错,但一调 Wait() 就 panic:sync: Cond.Wait with uninitialized mutex。更隐蔽的坑是:用 sync.Mutex 保护变量 A,却用 sync.RWMutex 绑定 cond,或多个 cond 共用不同锁实例——这会导致条件检查和等待之间出现竞态窗口,通知直接丢失。
- 必须传非 nil 锁指针:
var mu sync.Mutex; cond := sync.NewCond(μ) - 所有对共享变量(如
ready、queue、data)的读写,都得在mu.Lock()/mu.Unlock()内完成 -
cond.L就是那个锁,别另起一套锁逻辑
Signal 和 Broadcast 不是“发消息”,而是“拍肩膀叫人起来再自己看”
Signal() 唤醒队列头的一个 goroutine,Broadcast() 唤醒全部。但它们都不保证“唤醒即可用”——被唤醒者仍要抢锁、重检条件、失败则继续 Wait()。
立即学习“go语言免费学习笔记(深入)”;
- 适合用
Signal()的场景:生产者每次只塞一个任务,只需唤醒一个消费者 - 适合用
Broadcast()的场景:全局状态变更(如服务关闭、配置重载),所有等待者都需响应 - 慎用
Broadcast():上百 goroutine 同时Wait()时,一次广播会引发“惊群效应”,大量 goroutine 抢锁、检查、失败、再Wait(),锁争用飙升
最常被忽略的一点:你写的不是“等 ready == true”,而是“等别人调用 Signal() 或 Broadcast(),然后你自己在锁保护下再看一眼 ready 是不是真成了 true”。这个“再看一眼”,必须是循环,不能省,也不能外包给 Cond。


















