面试题答案
一键面试协同工作机制
- Mutex的作用:Mutex用于保护共享资源,确保在同一时间只有一个goroutine能够访问共享资源。在使用Cond时,通常先获取Mutex,以保证对共享数据的操作是线程安全的。
- Cond的作用:Cond依赖于Mutex,它提供了一种线程间同步的方式。当某个条件不满足时,goroutine可以通过Cond的
Wait
方法进入等待状态,同时释放Mutex,使得其他goroutine可以修改共享资源。当条件满足时,通过Signal
或Broadcast
方法唤醒等待的goroutine,被唤醒的goroutine会重新获取Mutex,然后继续执行。
优化示例
假设我们有一个生产者 - 消费者模型,生产者向共享队列中添加数据,消费者从队列中取出数据。
package main
import (
"fmt"
"sync"
)
type Queue struct {
data []int
mu sync.Mutex
cond *sync.Cond
}
func NewQueue() *Queue {
q := &Queue{}
q.cond = sync.NewCond(&q.mu)
return q
}
func (q *Queue) Enqueue(v int) {
q.mu.Lock()
defer q.mu.Unlock()
q.data = append(q.data, v)
q.cond.Broadcast()
}
func (q *Queue) Dequeue() int {
q.mu.Lock()
for len(q.data) == 0 {
q.cond.Wait()
}
v := q.data[0]
q.data = q.data[1:]
q.mu.Unlock()
return v
}
在这个例子中,Enqueue
方法添加数据后调用Broadcast
唤醒所有等待的消费者,Dequeue
方法在队列为空时调用Wait
等待,当被唤醒且获取到锁后继续从队列取数据。这样可以避免不必要的等待和空转,提升系统性能和资源利用率。
死锁场景及避免方法
- 死锁场景:
- 忘记解锁Mutex:在调用
Wait
方法前没有释放Mutex,或者在Signal
或Broadcast
后没有正确处理Mutex,可能导致死锁。例如:
- 忘记解锁Mutex:在调用
func wrongUsage() {
var mu sync.Mutex
cond := sync.NewCond(&mu)
mu.Lock()
cond.Wait() // 这里会导致死锁,因为Wait方法需要先释放锁
mu.Unlock()
}
- **重复获取锁**:如果在已经持有Mutex的情况下再次获取Mutex,也会导致死锁。例如:
func doubleLock() {
var mu sync.Mutex
mu.Lock()
mu.Lock() // 这里会导致死锁
mu.Unlock()
mu.Unlock()
}
- 避免方法:
- 严格遵循使用规范:在调用
Wait
方法前一定要先获取Mutex,并且Wait
方法会自动释放Mutex并挂起当前goroutine,被唤醒后会重新获取Mutex。在使用Signal
或Broadcast
时,要确保Mutex的状态是正确的,通常在持有Mutex的情况下调用这些方法。 - 代码审查:通过代码审查,仔细检查锁的获取和释放逻辑,确保没有重复获取锁或忘记释放锁的情况。
- 严格遵循使用规范:在调用