Cond
Go sync.Cond 详解
sync.Cond 是 Go 的一个同步原语,它用于协调 Goroutine 之间的等待和通知机制。sync.Cond 常用于实现线程间的等待、通知模式,比如生产者-消费者模式。它允许 Goroutine 在某个条件下等待,当条件满足时,其他 Goroutine 可以通知等待中的 Goroutine 继续执行。
1. 基本概念
- Condition Variable:条件变量是用于实现等待和通知的机制,Go 中通过
sync.Cond提供了这一功能。 - Wait():等待某个条件成立。如果条件不成立,当前 Goroutine 会阻塞,直到其他 Goroutine 通知它。
- Signal():通知一个等待中的 Goroutine。
- Broadcast():通知所有等待中的 Goroutine。
sync.Cond 依赖于 sync.Mutex 或 sync.RWMutex 来实现同步,通常需要和这些锁配合使用。
2. sync.Cond 的方法
Wait():将调用者添加到等待队列中,并且释放锁,当前 Goroutine 会被阻塞,直到其他 Goroutine 调用Signal()或Broadcast()来唤醒它。Signal():唤醒一个等待中的 Goroutine。Broadcast():唤醒所有等待中的 Goroutine。NewCond():创建一个新的条件变量。
3. sync.Cond 的使用示例
go
package main
import (
"fmt"
"sync"
)
var (
mu sync.Mutex // 互斥锁
cond = sync.NewCond(&mu) // 创建条件变量
counter int
)
func worker(id int) {
mu.Lock() // 获取锁
defer mu.Unlock()
for counter < 5 {
fmt.Printf("Worker %d: Waiting, counter=%d\n", id, counter)
cond.Wait() // 等待条件满足
}
fmt.Printf("Worker %d: Done, counter=%d\n", id, counter)
}
func main() {
var wg sync.WaitGroup
// 启动多个 Goroutine
for i := 1; i <= 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
worker(id)
}(i)
}
// 主 Goroutine 增加 counter,并通知所有等待的 Goroutine
mu.Lock()
counter = 5
cond.Broadcast() // 通知所有等待的 Goroutine
mu.Unlock()
wg.Wait()
}输出:
Worker 1: Waiting, counter=0
Worker 2: Waiting, counter=0
Worker 3: Waiting, counter=0
Worker 1: Done, counter=5
Worker 2: Done, counter=5
Worker 3: Done, counter=5在这个示例中:
worker函数模拟了多个 Goroutine 等待某个条件满足,直到counter达到 5。main函数修改counter的值,并通过cond.Broadcast()唤醒所有等待的 Goroutine。
4. sync.Cond 的工作原理
- 每个调用
Wait()的 Goroutine 都会被阻塞并放入条件变量的等待队列中,直到另一个 Goroutine 调用Signal()或Broadcast()来唤醒它们。 Wait()会在阻塞之前释放锁,允许其他 Goroutine 获取锁。等条件满足后,Wait()会重新获得锁并继续执行。Signal()或Broadcast()用于通知等待队列中的一个或多个 Goroutine,条件已经满足。
5. sync.Cond 与 sync.Mutex 的结合
由于 sync.Cond 依赖于 sync.Mutex 或 sync.RWMutex,因此通常会在条件变量等待时与这些锁一起使用。每次对共享资源的访问都需要先加锁,确保条件变量操作的安全。
6. Signal() 与 Broadcast() 的区别
- Signal():通知一个等待中的 Goroutine,唤醒它进行处理。
- Broadcast():通知所有等待中的 Goroutine,使它们都可以继续执行。
通常,Signal() 用于通知一个 Goroutine 条件满足,而 Broadcast() 用于通知所有条件满足。
7. sync.Cond 的性能考虑
sync.Cond 在高并发的场景下非常有用,尤其是在生产者-消费者模式中,它能够减少无谓的 CPU 占用,并且协调不同 Goroutine 的工作。但是,使用条件变量时需要小心避免:
- 虚假唤醒:即
Wait()被唤醒时,条件未必已经满足。因此,通常会使用for循环来判断条件。
go
for counter < 5 {
cond.Wait()
}这种方式能确保即使发生虚假唤醒,条件不满足时 Goroutine 仍然会继续等待。
8. 常见场景
- 生产者-消费者模式:生产者生产数据,消费者消费数据,消费者在没有数据时等待,生产者生产数据时通知消费者。
- 管道通信:Goroutine 通过管道传递消息,某些 Goroutine 等待数据的到来。
- 任务调度:一个 Goroutine 执行任务,其他 Goroutine 等待任务完成后执行后续操作。
9. 总结
sync.Cond是 Go 提供的条件变量,允许 Goroutine 等待某个条件满足,直到其他 Goroutine 通知它们继续执行。- 它结合了
sync.Mutex或sync.RWMutex来实现互斥操作,用于协调 Goroutine 之间的执行顺序。 - 使用
Wait()、Signal()和Broadcast(),可以实现复杂的同步和通信机制,尤其适用于生产者-消费者等模型。 - 虚假唤醒 是常见问题,因此在使用
Wait()时需要检查条件,而不仅仅是等待一次通知。
sync.Cond 是处理复杂同步问题的强大工具,特别适用于需要多个 Goroutine 之间等待和通知的场景。