首页 > 解决方案 > map var 快照的并发读/写

问题描述

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

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”,并随机产生“映射并发写入/读取”。我希望有人能向我解释为什么我的代码是错误的。

感谢您的时间。

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

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

感谢 xpare 和 nilsocket。

标签: go

解决方案


这个函数没有多次运行。许多其他函数正在使用相同的方法访问相同的“MapVar”,它随机产生一个“map concurrent write/read”

当您传递 to 的值时MapVar.MyMapMapSnapshot永远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类型相比是不同的int类型)。float32

请参考这个线程如何复制地图?


推荐阅读