MST

星途 面试题库

面试题:Go RWMutex锁在复杂数据结构下锁粒度优化

给定一个复杂的Go数据结构,例如嵌套的map结构,并且存在多个协程对其进行读写操作。请阐述如何精细地控制RWMutex锁的粒度,以避免锁争用,同时保证数据的一致性和正确性。请详细说明设计思路和关键代码片段。
10.4万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 细分锁的保护范围:对于嵌套的 map 结构,不要对整个数据结构使用一把大锁,而是根据数据访问模式,对频繁读写的子结构分别使用 RWMutex。例如,如果有一个 map[string]map[string]int 结构,且不同的外层 key 对应的内层 map 之间相互独立操作,那么可以为每个外层 key 对应的内层 map 单独分配一个 RWMutex
  2. 读多写少场景优化:因为 RWMutex 允许多个读操作同时进行,所以在读操作远多于写操作的场景下,这种锁机制能有效提升性能。在设计时,要确保读操作尽可能并行执行,而写操作能够安全地修改数据。
  3. 锁的嵌套使用:在某些复杂情况下,可能需要在不同层次的锁之间进行嵌套使用,但要注意避免死锁。例如,先获取外层锁,再获取内层锁,释放时顺序相反。

关键代码片段

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()
}

在上述代码中:

  1. InnerMap 结构体包含一个 map 和它自己的 RWMutex,用于保护内层 map 的读写操作。
  2. OuterMap 结构体包含一个 map,其值为 InnerMap 指针,并且也有自己的 RWMutex
  3. GetInnerMapSetInnerMap 方法用于对外层 map 进行操作,而 GetSet 方法用于对内层 map 进行操作,每个方法都根据需要正确地获取和释放锁,以确保数据的一致性和避免锁争用。