当在具有并发编写器的映射m
上进行测距时,包括可以从地图中删除的编写器,这样做是不是线程安全的?:
for k, v := range m { ... }
我正在考虑线程安全我需要阻止其他可能的编写者在我读取它时更改值v
,并且(当使用互斥锁时因为锁定是一个单独的步骤)验证密钥k
仍在地图中。例如:
for k := range m {
m.mutex.RLock()
v, found := m[k]
m.mutex.RUnlock()
if found {
... // process v
}
}
(假设其他编写者在更改m
之前是写锁定v
。)有更好的方法吗?
编辑添加:我知道地图不是线程安全的。但是,根据http://golang.org/ref/spec#For_statements的Go规范,它们在某种程度上是线程安全的(搜索“如果在迭代期间删除了尚未到达的映射条目”)。此页面表明使用range
的代码无需关心插入或删除地图的其他goroutines。我的问题是,这个线程安全性是否扩展到v
,这样我只能使用v
获取for k, v := range m
只读而没有其他线程 - 安全机制?我创建了一些测试代码,试图强制应用程序崩溃,以证明它不起作用,但甚至运行公然的线程不安全的代码(许多goroutines疯狂地修改相同的映射值没有锁定机制到位)我不能快去崩溃!
答案 0 :(得分:9)
不,地图操作不是原子/线程安全的,因为您问题的评论者指向the golang FAQ “Why are map operations not defined to be atomic?”。
为确保您的访问权限,我们鼓励您使用Go的 channels作为资源访问令牌。该通道用于简单地传递令牌。任何想要修改它的人都会从频道请求阻止或非阻止。完成使用地图后,它会将令牌传回通道。
迭代和使用地图应该足够简单和简短,所以你应该只使用一个令牌进行完全访问。
如果情况并非如此,并且您将地图用于更复杂的东西/资源消费者需要更多时间,那么您可以实现reader-vs writer-access-token。所以在任何给定的时间,只有一个作者可以访问地图,但是当没有作家处于活动状态时,令牌会被传递给任意数量的读者,他们不会修改地图(因此他们可以同时阅读)。
有关频道的介绍,请参阅the Effective Go docs on channels。
答案 1 :(得分:0)
您可以使用concurrent-map来处理并发性问题。
// Create a new map.
map := cmap.NewConcurretMap()
// Add item to map, adds "bar" under key "foo"
map.Add("foo", "bar")
// Retrieve item from map.
tmp, ok := map.Get("foo")
// Checks if item exists
if ok == true {
// Map stores items as interface{}, hence we'll have to cast.
bar := tmp.(string)
}
// Removes item under key "foo"
map.Remove("foo")