Golang map 读写加锁

Jackey Golang 86 次浏览 没有评论

在 Go 中,map 本身并不是线程安全的。如果在并发环境中对 map 进行读写操作,必须采取适当的同步措施来确保线程安全。

使用 sync.RWMutex

sync.RWMutex 提供了读写锁的功能,允许多个读取操作同时进行,但写入操作会独占锁。这种方式非常适合读多写少的场景。

type SafeMap struct {
    mu sync.RWMutex
    m  map[string]int
}

func NewSafeMap() *SafeMap {
    return &SafeMap{
        m: make(map[string]int),
    }
}

func (sm *SafeMap) Get(key string) (int, bool) {
    sm.mu.RLock()         // 读锁
    defer sm.mu.RUnlock() // 确保在函数返回时释放锁
    value, exists := sm.m[key]
    return value, exists
}

func (sm *SafeMap) Set(key string, value int) {
    sm.mu.Lock()         // 写锁
    defer sm.mu.Unlock() // 确保在函数返回时释放锁
    sm.m[key] = value
}

func (sm *SafeMap) Delete(key string) {
    sm.mu.Lock()         // 写锁
    defer sm.mu.Unlock() // 确保在函数返回时释放锁
    delete(sm.m, key)
}

func main() {
    sm := NewSafeMap()
    sm.Set("apple", 5)
    value, exists := sm.Get("apple")
    if exists {
        fmt.Println("Apple count is:", value)
    } else {
        fmt.Println("Apple not found in the map")
    }
}

使用 sync.Map

Go 标准库中的 sync.Map 提供了一种并发安全的 map,它内置了同步机制,适用于需要频繁并发访问的场景。与普通的 map 相比,sync.Map 的一些操作方法有所不同。

func main() {
    var sm sync.Map

    // 写入操作
    sm.Store("apple", 5)

    // 读取操作
    value, exists := sm.Load("apple")
    if exists {
        fmt.Println("Apple count is:", value)
    } else {
        fmt.Println("Apple not found in the map")
    }

    // 删除操作
    sm.Delete("apple")

    // 再次读取,确认删除
    value, exists = sm.Load("apple")
    if exists {
        fmt.Println("Apple count is:", value)
    } else {
        fmt.Println("Apple not found in the map")
    }
}

比较 sync.RWMutex 和 sync.Map

  • sync.RWMutex: 适合读多写少的场景,读操作不会相互阻塞。需要手动实现 map 的读写锁逻辑,更灵活,但也更容易出错。
  • sync.Map: 适合读写都频繁的场景,使用简单,内置了并发安全的机制,不需要手动管理锁。但是由于它的内部实现,某些操作可能会比使用普通的 map 加锁的方式慢,特别是在竞争激烈的场景下。

选择哪种方式取决于具体使用场景和性能需求。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

Go