面试题答案
一键面试1. 合理设置通道缓冲区大小
- 原则:根据数据流量和处理速度来设置。如果Goroutine产生数据的速度远快于线程处理速度,过小的缓冲区会导致Goroutine频繁阻塞等待线程读取数据;反之,过大的缓冲区可能会占用过多内存。
- 估算方法:可以根据经验值先设定一个初始值,例如100。然后通过性能测试工具(如
pprof
)来观察数据堆积情况,动态调整缓冲区大小。
2. 避免死锁
- 确保双向通信时的顺序一致:如果Goroutine和线程之间存在双向通信,在发送和接收操作上要保证顺序,避免一方等待另一方发送数据,而另一方又在等待这一方发送数据的情况。
- 使用
select
语句:在处理多个通道时,使用select
语句结合default
分支来避免阻塞。default
分支可以在通道操作不能立即执行时执行,防止死锁。
代码示例
package main
import (
"fmt"
"sync"
)
// 模拟CPU密集型计算
func cpuIntensiveTask(data int) int {
// 这里是复杂计算逻辑,简单示意
return data * data
}
func main() {
var wg sync.WaitGroup
// 设置通道缓冲区大小为100
dataCh := make(chan int, 100)
resultCh := make(chan int)
// Goroutine负责I/O操作及数据分发
go func() {
defer close(dataCh)
for i := 0; i < 10; i++ {
dataCh <- i
}
}()
// 线程(这里用Go协程模拟)负责CPU密集型计算
go func() {
defer close(resultCh)
for data := range dataCh {
result := cpuIntensiveTask(data)
resultCh <- result
}
}()
// 主线程收集结果
for result := range resultCh {
fmt.Println("Result:", result)
wg.Done()
}
wg.Wait()
}
在这个示例中:
dataCh
通道用于Goroutine向模拟线程发送数据,设置了缓冲区大小为100。resultCh
通道用于模拟线程向主线程返回计算结果。- 通过
for... range
循环来处理通道数据,保证数据的有序处理,避免死锁。同时,在发送完数据后及时关闭通道,确保接收方能够结束循环。