Golang RWMutex在地图内容上的编辑

时间:2018-08-04 17:38:23

标签: dictionary go mutex

我现在开始在RWMutex的Go项目中使用map,因为现在我有多个例程同时运行,并且在进行所有更改时都产生了疑问我的想法。

问题是,我知道在仅读取以允许其他例程执行相同任务时必须使用RLock,而在写以完全阻止该映射时必须使用Lock。但是,在编辑地图中先前创建的元素时我们应该怎么做?

例如...假设我有一个map[int]string,我在其中Lock,放在"hello "内,然后放入Unlock。如果我要向其中添加"world"怎么办?我应该做Lock还是可以做RLock

2 个答案:

答案 0 :(得分:4)

您应该从另一个角度解决问题。

您似乎理解得很好的简单经验法则是

  

当其中至少一个是修改时,您需要保护地图免受并发访问

现在真正的问题是什么构成地图的修改。

要正确回答,请注意,存储在地图中的值是不可寻址的-是设计使然。 之所以如此设计,完全是因为事实图在内部具有复杂的实现, might move values they contain in memory 提供(摊销)快速访问时间 当地图的结构由于其元素的插入和/或删除而发生变化时。

事实地图值不可寻址,这意味着您无法

m := make(map[int]string)
m[42] = "hello"
go mutate(&m[42]) // take a single element and go modifying it...
// ...while other parts of the program change _other_ values
m[123] = "blah blah"

不允许您这样做的原因是 插入操作m[123] = ...可能会触发移动 周围地图元素的存储,这可能 涉及移动由42键控的元素的存储 到内存中的其他位置—拉地毯 从goroutine的脚下 运行mutate函数。

因此,在Go中,地图实际上仅支持三种操作:

  • 插入或替换元素;
  • 读取元素;
  • 删除元素。

您不能“就地”修改元素-您只能 分三步走:

  1. 读取元素;
  2. 修改包含(已读取)副本的变量;
  3. 用修改后的副本替换元素。

您现在可以看到,步骤(1)和(3)只是地图访问, 因此,您的问题的答案是(希望)显而易见的: 步骤(1)必须至少在读锁下进行, 并且第(3)步应在写(独占)锁下完成。


相反,其他复合类型的元素- struct类型的数组(和切片)和字段- 没有限制图有:提供了存储 的“ enclosing”变量不重定位,可以 通过不同的goroutine同时更改其不同元素。

答案 1 :(得分:2)

由于更改映射中与键相关联的值的唯一方法是将更改后的值重新分配给同一键,即写入/修改,因此您必须获得读取锁-仅使用读取锁即可还不够。