英文:
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
}
// 全局计数器对象
var counters = single{
values: make(map[string]int64),
}
// 获取给定计数器的值
func Get(key string) int64 {
counters.mu.Lock()
defer counters.mu.Unlock()
return counters.values[key]
}
// 增加给定计数器名称的值
func Incr(key string) int64 {
counters.mu.Lock()
defer counters.mu.Unlock()
counters.values[key]++ // 竞争条件
return counters.values[key]
}
// 所有计数器
func All() map[string]int64 {
counters.mu.Lock()
defer counters.mu.Unlock()
return counters.values // 在上面的写入期间运行
}
我像这样使用该包:
counters.Incr("foo")
counters.Get("foo")
英文:
I have a simple package I am using to log stats during a program run and I found that go run -race
says there is a race condition in it. Looking at the program I'm not sure how I can have a race condition when every read and write is protected by a mutex. Can someone explain this?
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
}
I use the package like so:
counters.Incr("foo")
counters.Get("foo")
答案1
得分: 3
这里需要一个最小完整可验证的示例,但我认为你的问题出在All()
函数中:
// 所有计数器
func All() map[string]int64 {
counters.mu.Lock()
defer counters.mu.Unlock()
return counters.values // 在上面的写操作期间运行
}
这个函数返回一个map
,它没有对其进行复制,因此可以在互斥锁的保护之外访问它。
英文:
A Minimal Complete Verifiable Example would be useful here, but I think your problem is in All()
:
// All the counters
func All() map[string]int64 {
counters.mu.Lock()
defer counters.mu.Unlock()
return counters.values // running during write above
}
This returns a map
which does not make a copy of it, so it can be accessed outside the protection of the mutex.
答案2
得分: 2
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
方法。
英文:
All
returns the underlying map and the releases the lock, so the code using the map will have a data race.
You should return a copy of the map:
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
}
Or not have an All
method.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论