MST
星途 面试题库

面试题:Go语言Mutex在复杂并发场景中的应用优化

假设在一个高并发的Web服务器应用中,有多个goroutine需要频繁地读取和更新一个共享的用户会话信息。请设计一个基于Mutex的并发控制模型,同时考虑如何优化性能以减少锁争用。描述你的设计思路,并给出关键部分的代码实现。
39.3万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 使用Mutex进行基本保护:使用Go语言中的sync.Mutex来保护共享的用户会话信息,确保同一时间只有一个goroutine能够修改该信息。
  2. 读写分离优化:对于读操作远多于写操作的场景,可以考虑使用sync.RWMutex,允许多个goroutine同时进行读操作,只有写操作时才需要独占锁,从而减少锁争用。
  3. 减少锁的粒度:如果可能,将大的共享数据结构拆分成多个小的部分,每个部分使用单独的锁,这样不同的goroutine可以同时操作不同部分的数据,减少锁争用。
  4. 使用缓存:对于一些不经常变化的数据,可以使用缓存来存储,减少对共享数据的读取频率,从而减少锁争用。

关键部分代码实现

  1. 使用sync.Mutex的简单示例
package main

import (
    "fmt"
    "sync"
)

// UserSession 表示用户会话信息
type UserSession struct {
    data map[string]interface{}
    mu   sync.Mutex
}

// Get 获取会话数据
func (us *UserSession) Get(key string) interface{} {
    us.mu.Lock()
    defer us.mu.Unlock()
    return us.data[key]
}

// Set 设置会话数据
func (us *UserSession) Set(key string, value interface{}) {
    us.mu.Lock()
    defer us.mu.Unlock()
    if us.data == nil {
        us.data = make(map[string]interface{})
    }
    us.data[key] = value
}

func main() {
    var wg sync.WaitGroup
    session := UserSession{}

    // 模拟多个goroutine操作会话数据
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            key := fmt.Sprintf("key%d", id)
            session.Set(key, id)
            value := session.Get(key)
            fmt.Printf("Goroutine %d set and got: %v\n", id, value)
        }(i)
    }

    wg.Wait()
}
  1. 使用sync.RWMutex优化读操作
package main

import (
    "fmt"
    "sync"
)

// UserSession 表示用户会话信息
type UserSession struct {
    data map[string]interface{}
    mu   sync.RWMutex
}

// Get 获取会话数据
func (us *UserSession) Get(key string) interface{} {
    us.mu.RLock()
    defer us.mu.RUnlock()
    return us.data[key]
}

// Set 设置会话数据
func (us *UserSession) Set(key string, value interface{}) {
    us.mu.Lock()
    defer us.mu.Unlock()
    if us.data == nil {
        us.data = make(map[string]interface{})
    }
    us.data[key] = value
}

func main() {
    var wg sync.WaitGroup
    session := UserSession{}

    // 模拟多个读操作
    for i := 0; i < 10; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            key := fmt.Sprintf("key%d", id)
            value := session.Get(key)
            fmt.Printf("Goroutine %d read: %v\n", id, value)
        }(i)
    }

    // 模拟一个写操作
    wg.Add(1)
    go func() {
        defer wg.Done()
        session.Set("newKey", "newValue")
        fmt.Println("Write operation completed")
    }()

    wg.Wait()
}