面试题答案
一键面试性能下降原因分析
- 锁竞争激烈:在高并发场景下,大量的 goroutine 同时竞争
Mutex
锁,导致许多 goroutine 处于等待状态,增加了上下文切换的开销。 - 锁粒度大:如果对较大范围的代码块加锁,会使得只有一个 goroutine 能在该区域执行,限制了并发度。
优化 Mutex
锁使用提升性能策略
- 减小锁粒度:将大的锁保护区域拆分成多个小的区域,每个区域使用单独的锁。这样不同的 goroutine 可以同时访问不同区域的数据,提高并发度。
package main import ( "fmt" "sync" ) type SmallData struct { value int mu sync.Mutex } type BigData struct { data1 SmallData data2 SmallData } func main() { var wg sync.WaitGroup bigData := BigData{} for i := 0; i < 10; i++ { wg.Add(1) go func() { defer wg.Done() bigData.data1.mu.Lock() bigData.data1.value++ bigData.data1.mu.Unlock() bigData.data2.mu.Lock() bigData.data2.value++ bigData.data2.mu.Unlock() }() } wg.Wait() fmt.Printf("data1 value: %d, data2 value: %d\n", bigData.data1.value, bigData.data2.value) }
- 读写锁优化:如果数据读操作远多于写操作,可以使用
sync.RWMutex
。读操作时多个 goroutine 可以同时获取读锁,而写操作需要获取写锁,此时其他读写操作都被阻塞。package main import ( "fmt" "sync" ) type Data struct { value int mu sync.RWMutex } func read(data *Data, wg *sync.WaitGroup) { defer wg.Done() data.mu.RLock() fmt.Printf("Read value: %d\n", data.value) data.mu.RUnlock() } func write(data *Data, wg *sync.WaitGroup) { defer wg.Done() data.mu.Lock() data.value++ fmt.Printf("Write value: %d\n", data.value) data.mu.Unlock() } func main() { var wg sync.WaitGroup data := Data{} for i := 0; i < 5; i++ { wg.Add(1) go read(&data, &wg) } for i := 0; i < 2; i++ { wg.Add(1) go write(&data, &wg) } wg.Wait() }
预防死锁策略
- 避免嵌套锁:尽量不要在持有一个锁的情况下尝试获取另一个锁,除非顺序是固定且一致的。如果必须使用嵌套锁,确保在所有路径上以相同的顺序获取锁。
- 使用超时机制:在获取锁时设置一个超时时间,避免无限等待。Go 语言的
context
包可以实现这一点。package main import ( "context" "fmt" "sync" "time" ) type Resource struct { mu sync.Mutex } func accessResource(ctx context.Context, res *Resource, wg *sync.WaitGroup) { defer wg.Done() select { case <-ctx.Done(): fmt.Println("Timeout waiting for lock") return default: } res.mu.Lock() defer res.mu.Unlock() fmt.Println("Accessed resource") time.Sleep(2 * time.Second) } func main() { var wg sync.WaitGroup res := Resource{} ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) defer cancel() wg.Add(1) go accessResource(ctx, &res, &wg) wg.Wait() }
- 检测死锁:Go 语言的
runtime
包提供了死锁检测机制,在程序运行时如果发生死锁会打印相关信息。在开发和测试阶段可以利用这个特性及时发现死锁问题。例如,通过运行go run -race main.go
开启竞态检测,它也能帮助发现潜在的死锁情况。