面试题答案
一键面试优化策略
- 合理设置缓冲区大小:
- 分析生产者生产数据的速率和消费者处理数据的速率。如果生产者生产数据较快,而消费者处理数据相对较慢,设置一个合适大小的缓冲区可以减少生产者的阻塞。例如,可以通过前期的性能测试来估算平均的生产和消费速率,然后设置缓冲区大小。一般来说,可以先从一个较小的值开始尝试,如100,然后根据实际运行情况调整。
- 避免goroutine过度创建:
- 对于消费者,可以限制同时运行的goroutine数量。可以使用一个有缓冲的channel来控制消费者goroutine的并发数,例如,创建一个大小为N(根据系统资源和任务特性决定,如CPU核心数的倍数)的信号量channel,每个消费者goroutine启动前从这个channel获取一个信号,结束后再将信号放回。
- 优化数据处理:
- 尽量减少消费者处理数据过程中的不必要开销,如避免频繁的内存分配和释放,使用对象池等技术复用对象。
优化后的代码示例
package main
import (
"fmt"
"sync"
)
// 生产者函数,持续生成数据
func producer(dataChan chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
for i := 0; ; i++ {
dataChan <- i
}
}
// 消费者函数,处理数据
func consumer(dataChan <-chan int, semaphore chan struct{}, wg *sync.WaitGroup) {
defer wg.Done()
for data := range dataChan {
<-semaphore
// 模拟复杂计算
result := data * data
fmt.Printf("Consumer processed: %d, result: %d\n", data, result)
semaphore <- struct{}{}
}
}
func main() {
const bufferSize = 100
const consumerCount = 4
dataChan := make(chan int, bufferSize)
semaphore := make(chan struct{}, consumerCount)
var wg sync.WaitGroup
wg.Add(1)
go producer(dataChan, &wg)
for i := 0; i < consumerCount; i++ {
semaphore <- struct{}{}
wg.Add(1)
go consumer(dataChan, semaphore, &wg)
}
wg.Wait()
close(dataChan)
close(semaphore)
}
在上述代码中:
bufferSize
设置为100,作为数据channel的缓冲区大小。consumerCount
设置为4,表示同时运行的消费者goroutine数量。semaphore
用于控制消费者goroutine的并发数量,每个消费者在处理数据前从semaphore
获取信号,处理完成后放回信号,避免过多的并发导致系统资源耗尽。