sync.Cond.Broadcast不能直接用,因为Wait只挂起不检查条件,必须用for循环反复验证条件成立;Broadcast唤醒所有等待goroutine,但它们仍需抢锁并重新检查条件,否则可能读到脏数据或死锁。

sync.Cond.Broadcast 为什么不能直接用?
因为 Wait 不是“等通知”,而是“等条件成立”——它只负责挂起,不负责判断逻辑是否真满足。如果你调用 Broadcast 后就以为所有 goroutine 都能安全继续执行,大概率会读到脏数据或 panic。
- 必须在
Wait外层加 for 循环检查条件,比如for !condition { c.Wait() } -
Broadcast调用时无需持有锁,但修改条件变量(比如设置done = true)必须在锁内完成 - 唤醒后 goroutine 重新持锁返回,但此时条件可能又被其他 goroutine 改回 false(竞态),所以循环检查不可省
怎么写一个安全的 Broadcast 示例?
假设你有一组 worker goroutine 等待「任务队列已关闭」这个信号,用 Broadcast 通知全部退出:
var (
mu sync.Mutex
closed bool
cond = sync.NewCond(&mu)
)
// worker
go func() {
mu.Lock()
for !closed {
cond.Wait() // 自动 unlock + sleep + re-lock
}
mu.Unlock()
fmt.Println("worker exit")
}()
// main: 关闭信号
mu.Lock()
closed = true
cond.Broadcast() // 这里不用 hold mu
mu.Unlock()
注意:没加循环检查 !closed 就调 Wait,一旦 Broadcast 先于 Wait 发生,goroutine 就永远卡住。
Signal 和 Broadcast 的实际区别在哪?
不是“发一次 vs 发十次”的区别,而是「调度语义」不同:
立即学习“go语言免费学习笔记(深入)”;
-
Signal唤醒**任意一个**在Wait中阻塞的 goroutine(无序,不保证公平) -
Broadcast唤醒**所有**当前阻塞的 goroutine,但它们仍需逐个重新抢锁、检查条件 - 两者都不保证被唤醒 goroutine 立即执行——调度器决定谁先跑,锁竞争决定谁先进临界区
别指望 Broadcast 让所有人“同时开始干活”,它只是解除阻塞;后续行为仍由你写的条件逻辑和锁控制。
为什么官方不推荐用 sync.Cond?
不是它不能用,而是容易写出难维护的耦合逻辑:
- 条件变量通常要和多个共享状态(
bool、int、chan)手动配合,一漏检查就出错 - 相比
chan或context.WithCancel,Cond没有类型安全、没有缓冲、无法 select,调试时看不到等待队列长度 - 真实项目中,用
chan struct{}+close()实现广播更直观;单播场景用带缓冲的chan int更易测
除非你在写底层同步原语、或者已有大量基于 Cond 的历史代码,否则优先考虑 channel 或 context。










