在Go中使用范围获取值不是线程安全的吗?

当在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无需担心其他goroutine插入到地图或从地图删除的情况。我的问题是,此线程安全性是否扩展到v,这样我v 只能使用for k, v := range m并没有其他线程安全机制?我创建了一些测试代码来试图迫使应用程序崩溃以证明它不起作用,但是即使运行公然线程不安全的代码(很多goroutine在没有适当的锁定机制的情况下疯狂地修改了相同的映射值)进入崩溃!


POPMUISE
浏览 161回答 2
2回答

温温酱

否,地图操作不是原子/线程安全的,因为您的问题的评论者指出了golang FAQ“为什么地图操作未定义为原子的?” 。为了确保您的访问安全,建议您使用Go的频道作为资源访问令牌的一种方式。该通道用于简单地传递令牌。任何想要修改它的人都会从渠道请求-阻止或非阻止。处理完地图后,它将令牌传递回通道。遍历和使用地图应该足够简单且简短,因此您应该可以只使用一个令牌进行完全访问。如果不是这种情况,并且您将地图用于更复杂的东西/资源消耗者需要更多时间,则可以实现读写器访问令牌。因此,在任何给定时间,只有一个编写者可以访问该地图,但是当没有任何编写者处于活动状态时,令牌将传递给任意数量的读取者,而这些读取者不会修改该映射(因此他们可以同时读取)。

慕尼黑8549860

您可以使用并发映射为您处理并发难题。// 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 existsif ok == true {    // Map stores items as interface{}, hence we'll have to cast.    bar := tmp.(string)}// Removes item under key "foo"map.Remove("foo")
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go