初始化map元素,其中value是具有互斥锁golang

时间:2017-11-22 11:29:03

标签: go synchronization

我有一个地图,其中每个值都是指向另一个本身具有锁定的结构的指针。

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}
}

1 个答案:

答案 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}},并在首次使用后禁止复制。 在这里,你已经通过存储指针来做正确的事了 变量

现在你可以看到这样滚动很好:

  1. 访问地图本身以获取绑定到并发安全中的键的值 方式(比如持一把锁)。
  2. 锁定该变量上的互斥锁并对其进行操作。事实并非如此 完全涉及地图。
  3. 唯一剩下的可能问题如下。 假设您使用锁定保护对地图的访问权限。 所以你抓住锁,通过复制获得绑定到键的值 它变量,释放锁并使用。的副本 值。

    现在,当您正在使用该值的副本时,另一个 goroutine可以通过删除值或替换它来自由更新地图。

    虽然在你的情况下它在技术上很好 - 因为你的地图可以运行 指向变量的指针,复制指针很好 - 从你的程序语义的角度来看,这可能是不合适的, 这是你必须要思考的事情。 为了更清楚,一旦你有一个指向Stats的某个实例的指针 并锁定它,可以从地图中删除指向此实例的指针, 或者持有它的地图条目可以用另一个指针更新 - 指向Stats的另一个实例,所以一旦你完成了。{1}} 例如,它可能已经无法通过地图访问。