面试题答案
一键面试设计思路
- 细分锁的保护范围:对于嵌套的
map
结构,不要对整个数据结构使用一把大锁,而是根据数据访问模式,对频繁读写的子结构分别使用RWMutex
。例如,如果有一个map[string]map[string]int
结构,且不同的外层key
对应的内层map
之间相互独立操作,那么可以为每个外层key
对应的内层map
单独分配一个RWMutex
。 - 读多写少场景优化:因为
RWMutex
允许多个读操作同时进行,所以在读操作远多于写操作的场景下,这种锁机制能有效提升性能。在设计时,要确保读操作尽可能并行执行,而写操作能够安全地修改数据。 - 锁的嵌套使用:在某些复杂情况下,可能需要在不同层次的锁之间进行嵌套使用,但要注意避免死锁。例如,先获取外层锁,再获取内层锁,释放时顺序相反。
关键代码片段
package main
import (
"fmt"
"sync"
)
type InnerMap struct {
data map[string]int
mu sync.RWMutex
}
type OuterMap struct {
data map[string]*InnerMap
mu sync.RWMutex
}
func NewOuterMap() *OuterMap {
return &OuterMap{
data: make(map[string]*InnerMap),
}
}
func (om *OuterMap) GetInnerMap(key string) *InnerMap {
om.mu.RLock()
inner := om.data[key]
om.mu.RUnlock()
return inner
}
func (om *OuterMap) SetInnerMap(key string) {
om.mu.Lock()
if om.data[key] == nil {
om.data[key] = &InnerMap{
data: make(map[string]int),
}
}
om.mu.Unlock()
}
func (im *InnerMap) Get(key string) int {
im.mu.RLock()
value := im.data[key]
im.mu.RUnlock()
return value
}
func (im *InnerMap) Set(key string, value int) {
im.mu.Lock()
im.data[key] = value
im.mu.Unlock()
}
func main() {
outer := NewOuterMap()
var wg sync.WaitGroup
// 模拟写操作
wg.Add(1)
go func() {
defer wg.Done()
outer.SetInnerMap("subKey")
inner := outer.GetInnerMap("subKey")
if inner != nil {
inner.Set("innerKey", 42)
}
}()
// 模拟读操作
wg.Add(1)
go func() {
defer wg.Done()
inner := outer.GetInnerMap("subKey")
if inner != nil {
value := inner.Get("innerKey")
fmt.Println("Read value:", value)
}
}()
wg.Wait()
}
在上述代码中:
InnerMap
结构体包含一个map
和它自己的RWMutex
,用于保护内层map
的读写操作。OuterMap
结构体包含一个map
,其值为InnerMap
指针,并且也有自己的RWMutex
。GetInnerMap
和SetInnerMap
方法用于对外层map
进行操作,而Get
和Set
方法用于对内层map
进行操作,每个方法都根据需要正确地获取和释放锁,以确保数据的一致性和避免锁争用。