MST

星途 面试题库

面试题:Go每个请求一个goroutine场景下map的并发安全处理

在基于Go的Web应用中,采用每个请求一个goroutine的方式处理请求。现在有一个需求,需要在多个goroutine间共享一个map,这个map用于存储用户ID和对应的用户信息。请阐述有哪些方法可以保证对这个map的并发读写操作是安全的,并比较不同方法的优缺点,同时给出至少一种实现方式的示例代码。
12.4万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

保证并发读写安全的方法及优缺点

  1. 使用sync.Mutex

    • 优点:简单直观,适用于大多数场景,性能损耗相对较小,实现容易理解。
    • 缺点:粒度较大,同一时间只有一个goroutine能访问map,高并发场景下可能成为性能瓶颈。
  2. 使用sync.RWMutex

    • 优点:读操作可以并发执行,适用于读多写少的场景,能显著提高性能。
    • 缺点:实现相对复杂一些,写操作时会阻止所有读操作,写操作多的场景下可能影响性能。
  3. 使用sync.Map

    • 优点:专门为高并发场景设计,无需用户手动加锁,自动处理读写冲突,性能较高。
    • 缺点:相比sync.Mutexsync.RWMutex,占用内存更多,不支持遍历(Go 1.9版本之后提供了遍历方法,但使用相对复杂)。

示例代码(使用sync.RWMutex

package main

import (
    "fmt"
    "sync"
)

type UserInfo struct {
    Name string
    Age  int
}

var (
    userMap = make(map[string]UserInfo)
    rwMutex sync.RWMutex
)

func writeUser(id string, info UserInfo) {
    rwMutex.Lock()
    defer rwMutex.Unlock()
    userMap[id] = info
}

func readUser(id string) (UserInfo, bool) {
    rwMutex.RLock()
    defer rwMutex.RUnlock()
    info, exists := userMap[id]
    return info, exists
}

func main() {
    var wg sync.WaitGroup

    // 模拟写操作
    wg.Add(1)
    go func() {
        defer wg.Done()
        writeUser("1", UserInfo{Name: "Alice", Age: 25})
    }()

    // 模拟读操作
    wg.Add(1)
    go func() {
        defer wg.Done()
        info, exists := readUser("1")
        if exists {
            fmt.Printf("User Info: Name=%s, Age=%d\n", info.Name, info.Age)
        } else {
            fmt.Println("User not found")
        }
    }()

    wg.Wait()
}

示例代码(使用sync.Map

package main

import (
    "fmt"
    "sync"
)

type UserInfo struct {
    Name string
    Age  int
}

var userSyncMap sync.Map

func writeUser(id string, info UserInfo) {
    userSyncMap.Store(id, info)
}

func readUser(id string) (UserInfo, bool) {
    value, exists := userSyncMap.Load(id)
    if exists {
        return value.(UserInfo), true
    }
    return UserInfo{}, false
}

func main() {
    var wg sync.WaitGroup

    // 模拟写操作
    wg.Add(1)
    go func() {
        defer wg.Done()
        writeUser("1", UserInfo{Name: "Bob", Age: 30})
    }()

    // 模拟读操作
    wg.Add(1)
    go func() {
        defer wg.Done()
        info, exists := readUser("1")
        if exists {
            fmt.Printf("User Info: Name=%s, Age=%d\n", info.Name, info.Age)
        } else {
            fmt.Println("User not found")
        }
    }()

    wg.Wait()
}