Golang 在地图 [string]int 上与 sync.Mutex 比赛

Golang race with sync.Mutex on map[string]int

我有一个简单的包,我在程序 运行 期间使用它来记录统计信息,我发现 go run -race 说其中存在竞争条件。查看该程序,我不确定每次读写都受互斥锁保护时如何出现竞争条件。有人可以解释一下吗?

package counters

import "sync"

type single struct {
    mu     sync.Mutex
    values map[string]int64
}

// Global counters object
var counters = single{
    values: make(map[string]int64),
}

// Get the value of the given counter
func Get(key string) int64 {
    counters.mu.Lock()
    defer counters.mu.Unlock()
    return counters.values[key]
}

// Incr the value of the given counter name
func Incr(key string) int64 {
    counters.mu.Lock()
    defer counters.mu.Unlock()
    counters.values[key]++        // Race condition
    return counters.values[key]
}

// All the counters
func All() map[string]int64 {
    counters.mu.Lock()
    defer counters.mu.Unlock()
    return counters.values        // running during write above
}

我是这样使用包的:

counters.Incr("foo")
counters.Get("foo")

A Minimal Complete Verifiable Example 在这里很有用,但我认为你的问题在 All():

// All the counters
func All() map[string]int64 {
    counters.mu.Lock()
    defer counters.mu.Unlock()
    return counters.values        // running during write above
}

这个 returns 一个 map 不复制它,所以它可以在互斥锁的保护之外访问。

All return底层映射和释放锁,所以使用映射的代码会出现数据竞争。

您应该 return 地图的副本:

func All() map[string]int64 {
    counters.mu.Lock()
    defer counters.mu.Unlock()
    m := make(map[string]int64)
    for k, v := range counters.values {
        m[k] = v
    }
    return m    
}

或者没有All方法。