面试题答案
一键面试避免资源竞争和死锁的策略
- 合理使用Channel:
- 明确Channel的方向,使用带方向的Channel(如
chan<-
发送方向,<-chan
接收方向),这样在编译期就能检查出一些非法操作,比如在接收方向的Channel上进行发送操作。 - 确保发送和接收操作匹配,避免出现发送数据但无人接收,或者接收数据但无人发送的情况。
- 明确Channel的方向,使用带方向的Channel(如
- 同步机制:
- 使用
sync.WaitGroup
来等待所有Goroutine完成任务。它可以用来阻塞当前Goroutine,直到所有被等待的Goroutine都调用了Done
方法。 - 使用
sync.Mutex
对共享资源进行保护,在访问共享资源前加锁,访问完成后解锁,防止多个Goroutine同时访问共享资源导致数据竞争。
- 使用
- 控制Goroutine数量:避免创建过多的Goroutine导致系统资源耗尽,同时也能减少死锁的可能性。可以使用
worker pool
模式来限制同时运行的Goroutine数量。
代码示例
package main
import (
"fmt"
"sync"
)
func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
for j := range jobs {
fmt.Printf("Worker %d started job %d\n", id, j)
result := j * 2
fmt.Printf("Worker %d finished job %d, result: %d\n", id, j, result)
results <- result
}
}
func main() {
const numJobs = 5
jobs := make(chan int, numJobs)
results := make(chan int, numJobs)
var wg sync.WaitGroup
// 创建3个worker
for w := 1; w <= 3; w++ {
wg.Add(1)
go worker(w, jobs, results, &wg)
}
// 发送任务
for j := 1; j <= numJobs; j++ {
jobs <- j
}
close(jobs)
// 等待所有任务完成
go func() {
wg.Wait()
close(results)
}()
// 接收结果
for r := range results {
fmt.Printf("Result: %d\n", r)
}
}
在这个示例中:
worker
函数从jobs
通道接收任务,处理后将结果发送到results
通道,并使用WaitGroup
标记任务完成。main
函数创建了3个worker
,向jobs
通道发送任务,然后等待所有worker
完成任务并关闭results
通道,最后从results
通道接收结果。通过这种方式,既避免了资源竞争,也防止了死锁。