我有一个简单的程序包用于在程序运行期间记录统计信息,我发现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")
答案 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
方法。