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