地图内容编辑上的 Golang RWMutex
Golang RWMutex on map content edit
我开始在我的 Go 项目中使用 RWMutex
和 map
,因为现在我有多个例程 运行 同时进行所有更改为此,我想到了一个疑问。
我知道我们必须在仅读取时使用 RLock
以允许其他例程执行相同的任务,而在写入全块地图时必须使用 Lock
。但是我们在编辑之前在地图中创建的元素时应该做什么呢?
例如...假设我有一个 map[int]string
,我在其中做 Lock
,放入 "hello "
,然后放入 Unlock
。如果我想在其中添加 "world"
怎么办?我应该做 Lock
还是可以做 RLock
?
由于更改映射中与键相关联的值的唯一方法是将更改后的值重新分配给同一个键,即写/修改,所以你必须获得写锁——只需使用读锁是不够的。
你应该从另一个角度来看待问题。
一条您似乎理解得很好的简单经验法则是
You need to protect the map from concurrent accesses when at least one of them is a modification.
现在真正的问题是什么构成了对地图的修改。
为了正确回答这个问题,注意到 存储在地图中的值是不可寻址的 — 设计使然。
之所以以这种方式设计,仅仅是因为事实地图在内部具有复杂的实现方式
might move values they contain in memory
提供(摊销的)快速访问时间
当地图的结构由于元素的插入 and/or 删除而改变时。
事实地图值不可寻址意味着您不能
像
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 中,映射实际上只支持三种操作:
- 插入或替换元素;
- 读取一个元素;
- 删除一个元素。
您不能修改元素 "in place" — 您只能
分三步走:
- 读取元素;
- 修改包含(读取)副本的变量;
- 用修改后的副本替换元素。
如您现在所见,步骤 (1) 和 (3) 仅仅是地图访问,
所以你的问题的答案(希望)是显而易见的:
步骤(1)应至少在读锁下完成,
并且步骤(3)应在写(独占)锁下完成。
相比之下,其他复合类型的元素——
struct
类型的数组(和切片)和字段 —
没有限制地图有:提供存储
"enclosing" 变量没有重定位,可以
通过不同的 goroutines 同时改变它的不同元素。
我开始在我的 Go 项目中使用 RWMutex
和 map
,因为现在我有多个例程 运行 同时进行所有更改为此,我想到了一个疑问。
我知道我们必须在仅读取时使用 RLock
以允许其他例程执行相同的任务,而在写入全块地图时必须使用 Lock
。但是我们在编辑之前在地图中创建的元素时应该做什么呢?
例如...假设我有一个 map[int]string
,我在其中做 Lock
,放入 "hello "
,然后放入 Unlock
。如果我想在其中添加 "world"
怎么办?我应该做 Lock
还是可以做 RLock
?
由于更改映射中与键相关联的值的唯一方法是将更改后的值重新分配给同一个键,即写/修改,所以你必须获得写锁——只需使用读锁是不够的。
你应该从另一个角度来看待问题。
一条您似乎理解得很好的简单经验法则是
You need to protect the map from concurrent accesses when at least one of them is a modification.
现在真正的问题是什么构成了对地图的修改。
为了正确回答这个问题,注意到 存储在地图中的值是不可寻址的 — 设计使然。 之所以以这种方式设计,仅仅是因为事实地图在内部具有复杂的实现方式 might move values they contain in memory 提供(摊销的)快速访问时间 当地图的结构由于元素的插入 and/or 删除而改变时。
事实地图值不可寻址意味着您不能 像
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 中,映射实际上只支持三种操作:
- 插入或替换元素;
- 读取一个元素;
- 删除一个元素。
您不能修改元素 "in place" — 您只能 分三步走:
- 读取元素;
- 修改包含(读取)副本的变量;
- 用修改后的副本替换元素。
如您现在所见,步骤 (1) 和 (3) 仅仅是地图访问, 所以你的问题的答案(希望)是显而易见的: 步骤(1)应至少在读锁下完成, 并且步骤(3)应在写(独占)锁下完成。
相比之下,其他复合类型的元素——
struct
类型的数组(和切片)和字段 —
没有限制地图有:提供存储
"enclosing" 变量没有重定位,可以
通过不同的 goroutines 同时改变它的不同元素。