面试题答案
一键面试Go 语言中 sync.Cond 的原理
- 基本概念:
sync.Cond
是 Go 语言标准库中用于条件变量的结构体。它允许一个或多个 goroutine 等待特定条件满足,然后再继续执行。条件变量通常与一个互斥锁配合使用,用于在共享资源的状态发生变化时通知等待的 goroutine。 - 原理细节:
- 等待机制:当一个 goroutine 调用
Cond.Wait()
时,它会自动释放与之关联的互斥锁(这是必须在持有互斥锁的情况下调用Wait
的原因),然后该 goroutine 被阻塞,进入等待队列。当Cond.Signal()
或Cond.Broadcast()
被调用时,等待队列中的一个或多个 goroutine 会被唤醒。唤醒后,该 goroutine 会重新获取互斥锁,然后从Wait()
调用处继续执行。 - 通知机制:
Cond.Signal()
会唤醒等待队列中的一个 goroutine,而Cond.Broadcast()
会唤醒等待队列中的所有 goroutine。
- 等待机制:当一个 goroutine 调用
与互斥锁(Mutex)的关系
- 紧密协作:
sync.Cond
依赖于互斥锁来保护共享资源。在调用Cond.Wait()
、Cond.Signal()
或Cond.Broadcast()
之前,必须先获取互斥锁。这是因为共享资源的状态检查和修改需要在临界区内进行,以避免竞态条件。 - 互斥锁作用:互斥锁用于保证在同一时间只有一个 goroutine 能够访问共享资源,而
sync.Cond
则用于在共享资源状态发生变化时通知等待的 goroutine。
生产者 - 消费者模型示例
package main
import (
"fmt"
"sync"
)
type Queue struct {
items []int
cond *sync.Cond
mutex sync.Mutex
capacity int
}
func NewQueue(capacity int) *Queue {
q := &Queue{
items: make([]int, 0, capacity),
capacity: capacity,
}
q.cond = sync.NewCond(&q.mutex)
return q
}
// 生产者
func (q *Queue) Produce(item int) {
q.mutex.Lock()
defer q.mutex.Unlock()
for len(q.items) == q.capacity {
q.cond.Wait()
}
q.items = append(q.items, item)
fmt.Printf("Produced: %d\n", item)
q.cond.Signal()
}
// 消费者
func (q *Queue) Consume() int {
q.mutex.Lock()
defer q.mutex.Unlock()
for len(q.items) == 0 {
q.cond.Wait()
}
item := q.items[0]
q.items = q.items[1:]
fmt.Printf("Consumed: %d\n", item)
q.cond.Signal()
return item
}
你可以这样使用上述代码:
func main() {
queue := NewQueue(2)
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
for i := 0; i < 3; i++ {
queue.Produce(i)
}
}()
go func() {
defer wg.Done()
for i := 0; i < 3; i++ {
queue.Consume()
}
}()
wg.Wait()
}
在这个生产者 - 消费者模型中,Queue
结构体包含一个 sync.Cond
和一个 sync.Mutex
。生产者在队列满时调用 cond.Wait()
等待,消费者在队列空时调用 cond.Wait()
等待。当生产者生产了新的元素或消费者消费了元素后,通过 cond.Signal()
通知等待的 goroutine。这样就实现了高效的同步与通信。