6

当覆盖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插入到map中或从map中删除。我的问题是,这种线程安全性是否扩展到v,这样我就可以v 只读使用 onlyfor k, v := range m没有其他线程安全机制?我创建了一些测试代码来试图强制应用程序崩溃以证明它不起作用,但即使是公然运行线程不安全的代码(许多 goroutines 在没有锁定机制的情况下疯狂地修改相同的映射值)我不能去崩溃!

4

2 回答 2

9

不,映射操作不是原子/线程安全的,因为您问题的评论者指出了 golang 常见问题解答“为什么映射操作没有定义为原子的?”</a>。

为了确保您访问它的安全,我们鼓励您使用 Go 的通道作为资源访问令牌的一种手段。通道用于简单地传递令牌。任何想要修改它的人都会从频道请求 - 阻塞或非阻塞。处理完地图后,它将令牌传递回通道。

迭代和使用地图应该足够简单和简短,因此您应该可以只使用一个令牌进行完全访问。

如果不是这种情况,并且您将地图用于更复杂的东西/资源消费者需要更多时间使用它,您可以实现读取器与写入器访问令牌。因此,在任何给定时间,只有一个写入者可以访问地图,但是当没有写入者处于活动状态时,令牌会传递给任意数量的读取者,这些读取者不会修改地图(因此他们可以同时读取)。

有关频道的介绍,请参阅有关频道的 Effective Go 文档

于 2012-10-17T19:37:45.637 回答
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")
于 2014-10-05T16:50:55.427 回答