cond.Wait必须在while循环中调用,因虚假唤醒和竞态要求反复检查谓词;cond.Signal须在持锁修改状态后、释放锁前调用;cond.Wait与cond.Signal必须共用同一把锁;Condition适用于低频、长时间等待共享状态变更的场景。
cond.Wait 必须在 while 循环中调用
直接用 if 判断后调用 cond.wait() 是常见错误,会导致虚假唤醒(spurious wakeup)或条件被其他线程抢先修改后继续执行,引发数据错乱。posix 和 jvm 的 condition 实现都明确允许 cond.wait() 在无信号时返回,也允许一个 cond.signal() 唤醒多个线程(尤其在多核系统上)。
正确做法是:获取锁后,用 while 循环反复检查谓词(predicate),仅当条件真正满足时才退出循环:
lock.Lock()
defer lock.Unlock()
for len(queue) == 0 { // 谓词:队列为空
cond.Wait() // 自动释放 lock,唤醒后自动重新获取
}
item := queue[0]
queue = queue[1:]
- 不能写成
if len(queue) == 0 { cond.Wait() } - 即使只期望唤醒一个线程,也要循环检查——因为唤醒不等于条件成立
- Java 中
await()、Go 的Cond.Wait()、C 的pthread_cond_wait()全部适用该规则
cond.Signal 应在修改共享状态后、释放锁前调用
cond.Signal() 本身不操作共享数据,也不释放锁,它只是向等待队列发一个通知。如果在释放锁之后调用,可能出现「状态已变但通知丢失」的竞态:新线程抢先进入并再次阻塞,而旧线程的 signal 没有等待者接收。
典型安全顺序是:
lock.Lock()
// 修改共享状态:如 queue.push(item), count++
if len(queue) == 1 { // 刚从空变为非空,值得通知消费者
cond.Signal()
}
lock.Unlock()
- 必须在持有锁期间判断是否需要 signal —— 否则无法原子地观察状态+决定通知
- 不要在 unlock 后调用
cond.Signal(),哪怕看起来“逻辑更顺” - 若需唤醒所有等待者(例如关闭信号、批量就绪),用
cond.Broadcast()或cond.SignalAll(),但代价更高
cond.Wait 和 cond.Signal 必须共用同一把锁
Condition 不是独立同步原语,它必须与且仅与一个互斥锁绑定。例如 Go 的 sync.Cond{L: &mutex}、Java 的 ReentrantLock.newCondition()、C 的 pthread_cond_wait(&cond, &mutex) 都强制要求传入 mutex。跨锁使用会导致未定义行为,轻则死锁,重则内存破坏。
AI 舌诊面诊问诊医疗系统 + 诊疗商城系统源码是一套结合 AI 辅助诊断与商品转化的健康类程序,基于浩创云、ThinkPHP(PHP)开发,支持微信服务号与 H5 端,适合做中医舌诊面诊问答、健康建议推送以及配套诊疗商品的运营。
常见误用:
- 用
mutexA加锁,却传mutexB给cond.Wait()→ 直接 crash 或静默失败 - 一个
cond被多个不同锁的临界区复用 → 状态混乱,唤醒失效 - 忘记在
cond.Wait()返回后再次加锁(其实它已自动重锁,无需手动)→ 编译/运行时报错或逻辑跳过
何时该用 cond 而不是 channel 或 mutex + sleep?
Condition 适合「等待某个共享内存状态改变」的场景,尤其是当状态由多个线程协同维护、且通知频率低、等待时间长时。它比轮询 + time.Sleep() 节省 CPU,又比 channel 更贴近底层共享变量模型(比如 ring buffer、引用计数、文件描述符就绪等)。
但要注意:
- channel 更适合 Go 生态中 goroutine 间传递数据,语义清晰、自带缓冲与关闭机制
- 如果只是单次通知(如初始化完成),用
sync.Once或sync.WaitGroup更轻量 - Condition 无法跨进程使用;若需进程间通信,应选 POSIX 信号量或消息队列
最易被忽略的一点:Condition 的等待队列不保证 FIFO,signal 的唤醒顺序取决于调度器和实现,不要依赖唤醒先后做业务逻辑判定。










