面试题答案
一键面试- 设计思路:
- 使用
sync.Cond
结构体来实现条件变量。sync.Cond
需要与一个sync.Locker
(如sync.Mutex
)配合使用。 - 多个协程在访问共享资源前先获取锁。
- 当满足某些条件需要阻塞协程时,使用
cond.Wait()
方法,该方法会自动释放锁并阻塞当前协程,当被唤醒时会重新获取锁。 - 当条件满足时,使用
cond.Signal()
或cond.Broadcast()
方法来唤醒等待的协程。cond.Signal()
唤醒一个等待的协程,cond.Broadcast()
唤醒所有等待的协程。 - 为避免死锁,确保在调用
cond.Wait()
前获取了锁,并且在唤醒协程后,被唤醒的协程能够正确地重新获取锁并继续执行。同时,避免在没有获取锁的情况下调用cond.Signal()
或cond.Broadcast()
。 - 为避免不必要的资源竞争,确保对共享资源的读写操作都在锁的保护下进行。
- 使用
- 关键代码片段:
package main
import (
"fmt"
"sync"
"time"
)
var (
mu sync.Mutex
cond *sync.Cond
count int
)
func init() {
mu = sync.Mutex{}
cond = sync.NewCond(&mu)
count = 0
}
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
mu.Lock()
for count < 3 {
fmt.Printf("Worker %d waiting...\n", id)
cond.Wait()
}
fmt.Printf("Worker %d resumed. Count: %d\n", id, count)
// 对共享资源进行操作
count++
mu.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
wg.Add(1)
go worker(i, &wg)
}
time.Sleep(2 * time.Second)
mu.Lock()
count = 3
fmt.Println("Broadcasting to all workers...")
cond.Broadcast()
mu.Unlock()
wg.Wait()
fmt.Println("All workers finished. Final count:", count)
}
在上述代码中:
mu
是sync.Mutex
用于保护共享资源count
。cond
是sync.Cond
条件变量,通过NewCond
初始化并传入mu
。worker
函数是协程执行的函数,在获取锁后,通过cond.Wait()
等待条件满足(count
达到3)。- 在
main
函数中,启动多个协程后,通过修改count
并调用cond.Broadcast()
来唤醒所有等待的协程。