sync.Cond必须绑定已初始化的互斥锁,否则Wait会panic;需用sync.NewCond(&mutex)初始化,Wait必须在for循环中调用以应对虚假唤醒,Signal唤醒一个、Broadcast唤醒全部goroutine,且Wait前后需严格遵循加锁-检查-等待-操作-解锁流程。

Sync.Cond 的基本用法和初始化陷阱
sync.Cond 不是独立的同步原语,它必须绑定一个已存在的互斥锁(sync.Mutex 或 sync.RWMutex),否则调用 Wait 会 panic。常见错误是直接 new 一个空结构体:cond := &sync.Cond{} —— 这会导致运行时崩溃,因为内部的 L 字段为 nil。
正确做法只有两种:
- 用
sync.NewCond(&mutex)初始化,其中&mutex是已声明的sync.Mutex或sync.RWMutex地址 - 字段赋值必须显式完成:
cond := &sync.Cond{L: &mutex}(不推荐,易漏)
注意:sync.Cond 本身不保护任何数据,它只是协调等待逻辑;所有共享状态的读写仍需由你手动加锁保护。
Wait 必须在 for 循环中调用,不能用 if
cond.Wait() 返回时,条件不一定成立 —— 可能是虚假唤醒(spurious wakeup),也可能是其他 goroutine 提前 signal 但状态又被改回。所以永远不要写 if !condition { cond.Wait() }。
立即学习“go语言免费学习笔记(深入)”;
标准模式是:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
mutex.Lock()
defer mutex.Unlock()
for !condition {
cond.Wait()
}
// 此时 condition 一定为 true,且仍在锁内
常见翻车点:
- 把
cond.Wait()放在if里,导致错过条件变化或死锁 - 在
Wait()前没加锁,panic:“sync: inconsistent mutex state” -
Wait()返回后没重新检查条件,直接操作共享变量,引发竞态
Signal 和 Broadcast 的选择依据
cond.Signal() 唤醒**一个**正在等待的 goroutine,cond.Broadcast() 唤醒**所有**。选哪个取决于业务语义:
- 生产者-消费者模型中单个任务就绪 → 用
Signal更高效,避免惊群 - 状态全局变更(如关闭标志置位、配置重载完成)→ 必须用
Broadcast,否则部分 goroutine 永远等不到 - 多个等待者依赖同一条件但互斥执行(如抢锁)→
Signal合理;若条件满足后所有等待者都该继续 →Broadcast
注意:Signal 和 Broadcast **不要求持有锁**,但通常建议在锁内调用,确保唤醒动作与状态更新原子性。例如:先修改 ready = true,再 cond.Broadcast(),都在同一锁保护下。
Wait 期间锁自动释放,返回时自动重入
cond.Wait() 的行为是:先原子地释放传入的锁,挂起当前 goroutine;被唤醒后,**重新获取该锁**才返回。这意味着:
- 调用
Wait()前必须已持有锁,否则 panic - 返回时锁已被重新持有,无需再
Lock() - 如果多个 goroutine 同时
Wait(),它们被唤醒后会按调度顺序逐个抢锁,不是并发执行
典型误用:在 Wait() 后忘记检查条件,或试图在 Wait() 返回后立即 Unlock() —— 这会导致锁被重复释放 panic。正确节奏始终是:Lock → check condition in loop → Wait (auto unlock) → re-lock on return → use data → Unlock。
实际场景中,sync.Cond 很少单独出现,常配合 channel 或 context 实现更清晰的控制流;它的价值在于低开销唤醒,但心智负担高,稍不注意就掉坑里。

















