在Go语言中,sync.Cond
是一个用于协调多个goroutine之间等待与通知的同步原语,它通常与 sync.Mutex
或 sync.RWMutex
一起使用,以确保在条件等待和条件信号发送时的互斥访问。Cond
的 Wait
方法是 sync.Cond
类型中非常核心的一个方法,它用于阻塞当前goroutine,直到另一个goroutine通过调用 Signal
或 Broadcast
方法来唤醒它。
使用 sync.Cond
的基本步骤
创建并初始化:首先,你需要一个
sync.Mutex
(或sync.RWMutex
)和一个sync.Cond
实例。sync.Cond
必须在初始化时与一个已存在的互斥锁关联。锁定互斥锁:在调用
Wait
方法之前,必须首先锁定与Cond
关联的互斥锁。检查条件:在
Wait
方法调用之前,通常会有一个条件检查,以确定是否真的需要等待。调用
Wait
方法:调用Wait
方法会原子地释放互斥锁并使当前goroutine阻塞,直到另一个goroutine调用Signal
或Broadcast
方法。重新检查条件:从
Wait
返回后,互斥锁会被重新获取,此时应重新检查条件是否满足。解锁互斥锁:在条件满足并处理完相关逻辑后,应释放互斥锁。
示例代码
下面是一个使用 sync.Cond
的示例,模拟了一个简单的生产者-消费者问题,其中 Cond
用于控制消费者等待生产者生产数据。
package main
import (
"fmt"
"sync"
"time"
)
// 假设这是一个共享的数据结构
type Buffer struct {
items []int
cond *sync.Cond
mutex sync.Mutex
}
func NewBuffer(size int) *Buffer {
return &Buffer{
items: make([]int, 0, size),
cond: sync.NewCond(&sync.Mutex{}), // 注意这里传入的是互斥锁的指针
}
}
// 生产者
func (b *Buffer) Produce(item int) {
b.mutex.Lock()
defer b.mutex.Unlock()
b.items = append(b.items, item)
fmt.Printf("Produced: %d\n", item)
b.cond.Signal() // 通知一个等待的消费者
}
// 消费者
func (b *Buffer) Consume() {
b.mutex.Lock()
defer b.mutex.Unlock()
for len(b.items) == 0 {
fmt.Println("Waiting for item...")
b.cond.Wait() // 等待生产者生产数据
}
item := b.items[0]
b.items = b.items[1:]
fmt.Printf("Consumed: %d\n", item)
}
func main() {
buffer := NewBuffer(5)
// 启动生产者
go func() {
for i := 0; i < 10; i++ {
buffer.Produce(i)
time.Sleep(time.Second) // 模拟生产耗时
}
}()
// 启动消费者
for i := 0; i < 5; i++ {
go buffer.Consume()
time.Sleep(2 * time.Second) // 交错启动消费者
}
// 等待一段时间,确保所有goroutine都执行完毕
time.Sleep(10 * time.Second)
}
注意事项
- 在使用
sync.Cond
时,务必确保在调用Wait
方法之前已经锁定了互斥锁,并且在Wait
返回后重新检查条件,因为可能存在虚假唤醒(spurious wakeup)的情况。 Signal
方法唤醒一个等待的goroutine(如果有的话),而Broadcast
方法唤醒所有等待的goroutine。选择哪个方法取决于你的具体需求。- 示例中使用了
time.Sleep
来模拟生产者和消费者的不同速度,以及确保主goroutine在退出前等待其他goroutine完成。在实际应用中,可能需要更复杂的同步机制来确保程序的正确性和效率。
通过上述示例和解释,你应该对如何在Go语言中使用 sync.Cond
的 Wait
方法有了更深入的理解。希望这能帮助你在面试中脱颖而出,并在实际开发中更有效地利用Go的并发特性。