sync.cond.broadcast()前必须加锁修改状态,否则唤醒的goroutine会读到脏数据;wait必须用for循环重检条件;多逻辑组共用cond会导致惊群效应;cond仅适用于多对一通知,一对多任务分发应改用channel。

sync.Cond.Broadcast() 前没加锁修改状态,就会读到脏数据
调用 Broadcast() 本身不要求持有锁,但**修改被等待的条件变量必须在锁内完成**。如果先 Broadcast 再改状态,或者状态更新没锁保护,唤醒的 goroutine 一抢到锁就读到旧值。
- 错误写法:
cond.Broadcast(); done = true→ 唤醒时done还是false - 正确顺序:
mu.Lock(); done = true; cond.Broadcast(); mu.Unlock() - 更关键的是:
done的读取也必须在同把锁下(或用原子操作),否则-race直接报 data race
Wait 不套 for 循环,虚假唤醒或竞态会导致 panic
Wait() 返回不等于条件成立——可能是虚假唤醒,也可能是多个 goroutine 被 Broadcast() 唤醒后竞争锁,后抢到锁的那个发现条件又被别的 goroutine 改回 false。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 危险模式:
if len(queue) == 0 { cond.Wait() }→ 唤醒后直接queue[0],极大概率panic: index out of range - 安全模式:
for len(queue) == 0 { cond.Wait() },每次唤醒都重新检查 - 条件检查必须轻量、无副作用,比如
len(queue) > 0、atomic.LoadUint32(&state) == 1
多个逻辑组共用一个 Cond 实例,会触发惊群效应
上百个 goroutine 等待同一个 sync.Cond,一次 Broadcast() 就全拉起来抢锁、检查、再挂起——CPU 空转、锁争用暴涨、吞吐断崖下跌。
- 典型误用:不同优先级消费者共用一个
cond;限流器里所有等令牌的协程共享同一实例 - 替代方案:按语义拆分 Cond(如
highPrioCond/lowPrioCond),或改用 channel + select 分发 -
Signal()能缓解但不根治——它只唤醒一个,但无法保证唤醒的是“该干活那个”,业务得能容忍接力失败
Cond.L 是 nil 或类型错配,运行时直接 panic
sync.NewCond(nil) 合法但危险;Wait() 内部会解引用锁指针,nil 或类型不匹配都会 crash。
- 崩溃现场:
panic: sync: Cond.Wait with uninitialized mutex - 正确初始化:
mu := &sync.Mutex{}; cond := sync.NewCond(mu)(传指针,且生命周期要长于 cond) - 混用风险:用
sync.RWMutex{}初始化 cond,但 Wait 前调的是RUnlock()而非Lock()→ 行为未定义,可能死锁
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










