在Go中获取使用范围而非线程安全的值?

use*_*397 6 map go

当在m具有并发编写器的映射(包括可以从映射中删除的编写器)进行测距时,执行此操作是不是线程安全的?:

for k, v := range m { ... }
Run Code Online (Sandbox Code Playgroud)

我想要是线程安全的我需要阻止其他可能的编写器v在我读取它时更改值,并且(当使用互斥锁并且因为锁定是一个单独的步骤时)验证密钥k仍在映射中.例如:

for k := range m {
    m.mutex.RLock()
    v, found := m[k]
    m.mutex.RUnlock()
    if found {
        ... // process v
    }
}
Run Code Online (Sandbox Code Playgroud)

(假设其他编写器m在更改之前是写锁定的v.)有更好的方法吗?

编辑添加:我知道地图不是线程安全的.但是,根据http://golang.org/ref/spec#For_statements上的Go规范,它们在某种程度上是线程安全的(搜索"如果在迭代期间删除了尚未到达的映射条目").此页面表明使用的代码range无需关心插入或删除地图的其他goroutines.我的问题是,这个线程安全性是否延伸到v,这样我v 只能使用for k, v := range m而不能使用其他线程安全机制进行读取?我创建了一些测试代码,试图迫使一个应用程序崩溃,证明这是行不通的,但即使是运行公然线程安全的代码(很多够程奋力修改与替代无锁定机制同一地图价值的)我做不到快去碰撞!

Kis*_*aki 9

不,映射操作不是原子/线程安全的,因为你的问题的评论者指向golang FAQ"为什么地图操作没有定义为原子?".

为了保护您的访问权限,建议您使用gos通道作为资源访问令牌的一种方式.该通道用于简单地传递令牌.任何想要修改它的人都会从频道请求阻止或非阻止.完成使用地图后,它会将令牌传递回通道.

迭代和使用地图应该足够简单和简短,所以你应该只使用一个令牌进行完全访问.

如果情况并非如此,并且您将地图用于更复杂的东西/资源消费者需要更多时间,那么您可以实现reader-vs writer-access-token.所以在任何给定的时间,只有一个作者可以访问地图,但是当没有作家处于活动状态时,令牌会被传递给任何数量的读者,他们不会修改地图(因此他们可以同时阅读).

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