并发读/写map var快照

时间:2018-10-15 08:26:09

标签: go

我遇到一种我无法理解的情况。在我的代码中,我使用函数需要读取一个映射(但不能写入,仅循环遍历此映射中现有数据的快照)。有我的代码:

type MyStruct struct {
   *sync.RWMutex
   MyMap map[int]MyDatas
}

var MapVar = MyStruct{ &sync.RWMutex{}, make(map[int]MyDatas) }

func MyFunc() {
   MapVar.Lock()
   MapSnapshot := MapVar.MyMap
   MapVar.Unlock()
   for _, a := range MapSnapshot { // Map concurrent write/read occur here
      //Some stuff
   }
}

main() {
   go MyFunc()
}

函数“ MyFunc”在go例程中运行,只有一次,此功能没有多次运行。许多其他功能使用相同的方法访问相同的“ MapVar”,并且随机产生“映射并发写入/读取”。我希望有人能向我解释为什么我的代码是错误的。

谢谢您的时间。

edit:为澄清起见,我只是问为什么我的范围MapSnapshot会产生并发的地图写/读。由于无法使用同步互斥锁将真正的全局变量(MapVar)保存在本地变量(MapSnapshot)中,因此我无法理解如何同时使用此地图。

编辑:已解决。要在不使用相同引用的情况下将映射的内容复制到新变量中(从而避免映射并发读/写),我必须遍历它,并使用for循环将每个索引和内容写入新映射。

感谢xpare和nilsocket。

1 个答案:

答案 0 :(得分:0)

  

此功能没有多次运行。许多其他函数使用相同的方法访问相同的“ MapVar”,并随机产生“地图并发写入/读取”

当您将MapVar.MyMap的值传递给MapSnapshot时,Map concurrent write/read将永远不会出现,因为操作是用互斥量包装的。

但是在循环中,由于实际上在循环中进行读取过程,因此可能会发生错误。因此最好也用互斥体包装循环。

MapVar.Lock() // lock begin

MapSnapshot := MapVar.MyMap
for _, a := range MapSnapshot {
   // Map concurrent write/read occur here
   // Some stuff
}

MapVar.Unlock() // lock end

更新1

以下是我对您的论点的答复:

  

这个for循环要花很多时间,这个循环中有很多东西,所以锁定会降低其他例程的速度

根据您的声明The function "MyFunc" is run in a go routine, only once, there is no multiple runs of this func,那么我认为让MyFunc作为goroutine执行不是一个好选择。

为提高性能,最好使循环内的过程在goroutine中执行。

func MyFunc() {
    for _, a := range MapVar.MyMap {
        go func(a MyDatas) {
           // do stuff here
        }(a)
    }
}

main() {
    MyFunc() // remove the go keyword
}

更新2

如果您确实要复制 MapVar.MyMap到另一个对象中,将其传递到另一个变量将无法解决(map与{{1} },int或其他原始类型。

请参考该线程How to copy a map?