在地图上使用sync.Mutex进行Golang比赛[string] int

时间:2016-12-14 23:19:17

标签: 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{
    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")

2 个答案:

答案 0 :(得分:2)

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

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

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

答案 1 :(得分:1)

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