sync.Cond不是信号量,而是配合互斥锁实现条件等待的协作机制;必须与sync.Mutex联用,Wait会自动释放并重新获取锁,且需循环检查条件,Signal唤醒一个、Broadcast唤醒全部等待goroutine。

Go 里没有 sync.Cond 对应的传统“信号量”概念
很多人搜“Go 信号量 + 条件变量”,其实是混淆了概念:sync.Cond 不是信号量(semaphore),也不提供类似 pthread 的 wait/signal 原语的完整语义。它只是配合 sync.Mutex 或 sync.RWMutex 实现“等待某个条件成立”的协作机制,底层靠的是 goroutine 的阻塞/唤醒,不是计数型资源控制。
如果你真需要信号量(比如限制并发数、池化资源),该用 semaphore 模式:用带缓冲的 channel(make(chan struct{}, N))或第三方库如 golang.org/x/sync/semaphore。而 sync.Cond 只适合“一个或多个 goroutine 等待某状态改变后被唤醒”这类场景,比如生产者-消费者中的空/满缓冲通知。
sync.Cond 必须和互斥锁一起用,且 Wait() 会自动释放锁
sync.Cond 不能独立存在,必须绑定一个已初始化的 sync.Locker(通常是 *sync.Mutex)。调用 c.Wait() 时,它会原子性地:释放关联锁 → 挂起当前 goroutine → 等待被 Signal() 或 Broadcast() 唤醒 → 唤醒后重新获取锁才返回。
- 忘记在
Wait()前加锁,会导致 panic:“sync: Cond.Wait with uninitialized mutex” - 在
Wait()返回后,必须重新检查条件是否真的满足——因为可能被虚假唤醒(spurious wakeup),也可能被其他 goroutine 抢先修改了状态 - 不要在
for循环外直接调用Wait();标准写法是for !condition { c.Wait() }
示例片段:
立即学习“go语言免费学习笔记(深入)”;
本文档主要讲述的是Android传感器编程;传感器是一种物理装置或生物器官,能够探测、感受外界的信号、物理条件(如光、热、湿度)或化学组成(如烟雾),并将探知的信息传递给其它装置或器官。同时也可以说传感器是一种检测装置,能感受被测量的信息,并能将检测的感受到的信息,按一定规律变换成为电信号或其它所需形式的信息输出,以满足信息的传输、处理、存储、显示、记录和控制等要求。它是实现自动检测和自动控制的首要环节。感兴趣的朋友可以过来看看
var mu sync.Mutex
var cond *sync.Cond
func init() {
cond = sync.NewCond(&mu)
}
// 等待 data != nil
func waitForData() {
mu.Lock()
defer mu.Unlock()
for data == nil {
cond.Wait() // 自动 unlock → sleep → lock on wake
}
// 此时 data 已非 nil,且 mu 仍被持有
}
Signal() 和 Broadcast() 的唤醒行为差异很关键
Signal() 最多唤醒一个正在 Wait() 的 goroutine(如果有),Broadcast() 唤醒所有。但注意:它们都不保证“立即执行”,只是把 goroutine 从等待队列移到可运行队列;实际调度仍由 Go runtime 决定。
- 用
Signal()适合“一对一”通知,比如单个消费者等一个新任务,生产者发完就 Signal - 用
Broadcast()适合“一对多”或状态变化影响多个等待者,比如关闭信号、缓存失效等 - 不要假设
Signal()一定能唤醒“刚 Wait 的那个”——goroutine 调度顺序不保证,可能唤醒的是更早进入等待的 goroutine - 如果唤醒后逻辑依赖“谁被唤醒”,说明设计有误;
sync.Cond是无状态通知,不是消息队列
常见陷阱:忘记加锁就调用 Signal() 或 Broadcast()?其实不用
Signal() 和 Broadcast() 本身不要求持有锁(它们只操作等待队列,不读写共享数据),所以可以不加锁调用。但现实中,你几乎总得在修改条件变量所保护的状态之后才发通知,而这个“修改状态”操作通常需要锁保护。
- 正确顺序:加锁 → 修改共享状态(如
data = newData)→ 调用cond.Signal()或cond.Broadcast()→ 解锁 - 错误做法:先解锁再
Signal()—— 虽然不会 panic,但可能导致等待者醒来后发现状态还没更新(竞态) - 也别在锁外修改状态再锁内
Signal(),除非你能确保修改是原子的(比如单纯赋值int64在 64 位平台且对齐)
真正容易被忽略的是:条件变量没有“记忆”。如果 Signal() 发生在没人 Wait() 的时候,这次通知就丢了。所以通知逻辑和等待逻辑的时序必须靠程序逻辑保证,不能依赖 Cond 缓存信号。









