Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/logging/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Map 在Go中使用范围获取值不是线程安全的吗?_Map_Go - Fatal编程技术网

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

Map 在Go中使用范围获取值不是线程安全的吗?,map,go,Map,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 {

在具有并发写入程序(包括可以从映射中删除的写入程序)的映射
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
    }
}
(假设其他写入程序在更改
v
之前正在写入锁定
m
)有更好的方法吗


编辑添加:我知道地图不是线程安全的。然而,根据Go规范at,它们在某种程度上是线程安全的(搜索“如果在迭代过程中删除了尚未到达的映射条目”)。此页面表明,使用
范围的代码不必担心其他goroutine插入地图或从地图中删除。我的问题是,这个线程安全性是否扩展到了
v
,这样我就可以只使用
k,v:=range m
而不使用其他线程安全机制来获取
v
进行读取?我创建了一些测试代码,试图强制应用程序崩溃,以证明它不起作用,但即使运行明显的线程不安全代码(许多goroutines在没有锁定机制的情况下疯狂修改相同的map值),我也无法崩溃

不,映射操作不是原子/线程安全的,正如您问题的评论者所指出的那样

为了确保您的访问安全,我们鼓励您使用Go的作为资源访问令牌的一种方式。通道仅用于传递令牌。任何想要修改它的人都会请求通道阻塞或非阻塞。使用映射完成后,它将令牌传递回通道

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

如果情况并非如此,并且您使用映射来处理更复杂的内容/资源使用者需要更多的时间,那么您可以实现读写器访问令牌。因此,在任何给定时间,只有一个写入程序可以访问地图,但当没有任何写入程序处于活动状态时,令牌将传递给任意数量的读卡器,这些读卡器不会修改地图(因此它们可以同时读取)

有关通道的介绍,请参阅。

您可以使用它来处理并发问题

// 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")

只是一句评论(对于可能已经知道这一点的OP读者来说要比其他读者少):谢谢你的回复!我编辑了我的问题,以强调我想知道对于k,v:=range m{…}
而言,对于
v
而言,仅用于阅读是否是线程安全的。Go规范指出,
range
对于插入到映射或从映射中删除的其他goroutine来说是线程安全的。我知道频道,但是如果使用
range
获取
v
已经是线程安全的,那就是我所需要的。好吧,再看看,我发现你的答案是正确的。似乎为了线程安全,如果其他goroutine将写入(更新、删除或修改)正在迭代的相同密钥,则有必要在使用
range
之前锁定整个映射。我提到的Go规范中的一节讨论了从它所覆盖的映射中删除或插入的单个线程,以及该线程如何影响自身。