Debugging Golang与地图上的sync.Mutex[字符串]int进行比赛

Debugging Golang与地图上的sync.Mutex[字符串]int进行比赛,debugging,go,race-condition,Debugging,Go,Race Condition,我有一个简单的软件包,用来记录程序运行期间的统计数据,我发现go run-race说其中有一个竞赛条件。看着这个程序,我不确定当每次读写都受到互斥保护时,我怎么会有竞争条件。有人能解释一下吗 package counters import "sync" type single struct { mu sync.Mutex values map[string]int64 } // Global counters object var counters = single

我有一个简单的软件包,用来记录程序运行期间的统计数据,我发现
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")

在这里,一个最小的完整的可验证示例会很有用,但我认为您的问题在于
All()


这将返回一个
映射
,该映射不复制它,因此可以在互斥锁的保护之外访问它。

All
返回基础映射并释放锁,因此使用映射的代码将有数据竞争

您应该返回地图的副本:

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
方法

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    
}