我有一个地图,其中每个值都是指向另一个本身具有锁定的结构的指针。
type StatMap map[string]*Stats
type Stats struct {
sync.RWMutex
someStats, someMoreStats float64
}
我已经实现了一个方法,我将StatMap打包到另一个结构中,并为整个地图都有一个Mutex锁,但是我期望从数百个goroutine中同时修改地图中的每个条目,所以它会更有效锁定地图中的每个元素,以便两个或多个goroutine可以读取和修改parallell中的条目值。
我想知道的是,无论何时出现新密钥,我都可以在地图中初始化新条目?我不能锁定条目,如果它已经不在地图中,并且我无法检查它是否在地图中(据我所知),以防另一个goroutine当前正在修改该条目。
我不知道在运行之前地图中的键是什么。
我当前的实现(导致数据竞争):
initializeStatMap("key")
statMap["key"].Lock()
// . . .
func initializeStatMap(key string) {
if statMap[key] != nil {
return
}
statMap[key] = &Stats{someStats: 0, someMoreStats: 0}
}
答案 0 :(得分:1)
Go的地图语义如下:
地图存储值(不是变量),这就是为什么这些值是 不是可以说的,这就是为什么你不能做像
这样的事情type T struct {
X int
}
m := make(map[int]T)
m[0] = T{}
m[0].x = 42 // won't compile
这个要求主要来自于地图的存在 一个复杂的动态数据结构,应该允许它的特殊性 物理移动它包含的值的实现 在记忆中 - 当进行再平衡等时。
这就是为什么地图支持的唯一三个操作是添加 (或替换)其元素,将它们取回并删除它们。
地图对于并发使用是不安全的,所以为了做这三个中的任何一个 同时在同一个地图上进行操作,你需要保护它 不管怎样。
因此,一旦您从地图中读取了一个值,就可以进行编排
并发访问 it 是一个完全相同的故事,
在这里,我们面临着关于地图语义的另一个事实:
因为它保留了值并且可以在内存中自由地复制它们,
它不允许在地图中保留您想要的任何内容
参考语义。例如,保持值是不正确的
你的Stats
类型直接在地图中 - 因为它们嵌入了实例
sync.Mutex
的{{1}},并在首次使用后禁止复制。
在这里,你已经通过存储指针来做正确的事了
变量
现在你可以看到这样滚动很好:
唯一剩下的可能问题如下。 假设您使用锁定保护对地图的访问权限。 所以你抓住锁,通过复制获得绑定到键的值 它变量,释放锁并使用。的副本 值。
现在,当您正在使用该值的副本时,另一个 goroutine可以通过删除值或替换它来自由更新地图。
虽然在你的情况下它在技术上很好 - 因为你的地图可以运行
指向变量的指针,复制指针很好 - 从你的程序语义的角度来看,这可能是不合适的,
这是你必须要思考的事情。
为了更清楚,一旦你有一个指向Stats
的某个实例的指针
并锁定它,可以从地图中删除指向此实例的指针,
或者持有它的地图条目可以用另一个指针更新 -
指向Stats
的另一个实例,所以一旦你完成了。{1}}
例如,它可能已经无法通过地图访问。