面试题答案
一键面试可能出现的性能问题
- 阻塞问题:如果收集协程关闭通道前,其他协程已经在通道上阻塞读取,关闭通道可能导致不必要的等待,影响整体性能。
- 资源浪费:其他协程在通道关闭后继续读取,会导致额外的处理开销,虽然Go语言的通道关闭后读取不会报错,但会消耗资源。
优化方案
- 同步机制:使用
sync.WaitGroup
来确保收集协程完成数据收集并关闭通道后,其他协程再开始读取。 - 条件变量:使用
sync.Cond
结合sync.Mutex
来通知其他协程通道已准备好读取。
代码模拟
未优化版本
package main
import (
"fmt"
)
func worker(id int, resultChan chan int) {
resultChan <- id * id
}
func collector(resultChan chan int, numWorkers int) {
for i := 0; i < numWorkers; i++ {
worker(i, resultChan)
}
close(resultChan)
}
func main() {
numWorkers := 10
resultChan := make(chan int)
go collector(resultChan, numWorkers)
for result := range resultChan {
fmt.Println("Received result:", result)
}
}
优化版本(使用 sync.WaitGroup)
package main
import (
"fmt"
"sync"
)
func worker(id int, resultChan chan int, wg *sync.WaitGroup) {
defer wg.Done()
resultChan <- id * id
}
func collector(resultChan chan int, numWorkers int, wg *sync.WaitGroup) {
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go worker(i, resultChan, wg)
}
go func() {
wg.Wait()
close(resultChan)
}()
}
func main() {
numWorkers := 10
resultChan := make(chan int)
var wg sync.WaitGroup
collector(resultChan, numWorkers, &wg)
for result := range resultChan {
fmt.Println("Received result:", result)
}
}
验证优化效果
- 性能分析工具:可以使用Go语言自带的
pprof
工具来分析优化前后的性能差异。例如,在程序中添加如下代码:
package main
import (
"fmt"
"net/http"
_ "net/http/pprof"
"sync"
)
func worker(id int, resultChan chan int, wg *sync.WaitGroup) {
defer wg.Done()
resultChan <- id * id
}
func collector(resultChan chan int, numWorkers int, wg *sync.WaitGroup) {
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go worker(i, resultChan, wg)
}
go func() {
wg.Wait()
close(resultChan)
}()
}
func main() {
numWorkers := 10
resultChan := make(chan int)
var wg sync.WaitGroup
go func() {
http.ListenAndServe("localhost:6060", nil)
}()
collector(resultChan, numWorkers, &wg)
for result := range resultChan {
fmt.Println("Received result:", result)
}
}
然后通过 go tool pprof http://localhost:6060/debug/pprof/profile
来获取性能分析报告,对比优化前后的CPU、内存使用情况等指标。
- 简单测试:通过在代码中添加计时逻辑,计算优化前后程序运行的总时间,直观感受优化效果。例如:
package main
import (
"fmt"
"sync"
"time"
)
func worker(id int, resultChan chan int, wg *sync.WaitGroup) {
defer wg.Done()
resultChan <- id * id
}
func collector(resultChan chan int, numWorkers int, wg *sync.WaitGroup) {
for i := 0; i < numWorkers; i++ {
wg.Add(1)
go worker(i, resultChan, wg)
}
go func() {
wg.Wait()
close(resultChan)
}()
}
func main() {
numWorkers := 10
resultChan := make(chan int)
var wg sync.WaitGroup
start := time.Now()
collector(resultChan, numWorkers, &wg)
for result := range resultChan {
fmt.Println("Received result:", result)
}
elapsed := time.Since(start)
fmt.Printf("Total time: %s\n", elapsed)
}
对比未优化版本和优化版本的总运行时间,验证优化效果。