Golang,本地映射的竞态条件

huangapple go评论91阅读模式
英文:

Golang, race condition with local map

问题

我似乎并不完全理解Go语言中的地图(maps)。

我有以下代码:

fetch := map[string]int{一些数据}

for 条件 {
    fetchlocal := map[string]int{}

    for 键, 值 := range fetch {
        if 条件 {
            fetchlocal[键] = 值
        }
    }

    go 线程函数(fetchlocal)
}

现在,无论线程函数(threadfunc)在哪里使用fetchlocal变量,Go(go -race)都会显示警告:数据竞争(data race)。我已经遇到了一些恐慌。但是为什么呢?fetchlocal变量没有被任何其他goroutine使用。

有人可以给我解释一下吗?

英文:

I don't seem to totally understand maps in Go.

I have this code:

fetch := map[string]int{some data}

for condition {
    fetchlocal := map[string]int{}

    for key, value := range fetch {
        if condition {
            fetchlocal[key] = value
        }
    }

    go threadfunc (fetchlocal)
}

Now wherever the threadfunc function uses the fetchlocal variable Go (go -race) says warning: data race. I also got a few panics already. But why? The fetchlocal variable isn't used by any other goroutine.

Can someone enlighten me, please?

答案1

得分: 3

我假设你的fetch := map[string]int{some data}实际上应该是:fetch := map[string][]int{..some data..}

要使这个成为一个竞争条件,threadfunc必须在fetchlocal中更改一个值,或者其他地方必须更改fetch中的值。

也就是说,一个切片实际上是这样的:

type SliceHeader struct {
    Data uintptr
    Len  int
    Cap  int
}

当你将元素从一个映射复制到另一个映射时,你并没有对切片进行深拷贝(你只是创建了一个具有相同Data、Len、Cap的新结构体),也就是说fetch["foo"].Data == fetchlocal["foo"].Data

因此,你可以说fetch[someExistingKey] = someNewValue,这不会与threadfunc竞争,但如果你说fetch[someExistingKey][x] == foobarfetchlocal[someExistingKey][x] == foobar,就会出现竞争。

如果threadfunc需要修改fetchlocal,你可以将内部循环更改为:

for key, value := range fetch {
    if condition {
        newVal := make([]int, len(value))
        copy(newVal, val)
        fetchlocal[key] = newVal
    }
}

或者,在修改之前,在threadfunc内部根据需要进行复制。

附注:如果你分享了实际的threadfunc或在这两个循环运行时修改fetch的代码,我们将能够提供更多帮助。

英文:

I'm assuming your fetch := map[string]int{some data} was actually supposed to be: fetch := map[string][]int{..some data..}.

For this to be a race threadfunc must be changing a value within fetchlocal or something else must be changing the value within fetch.

This is to say a slice is a actually:

type SliceHeader struct {
        Data uintptr
        Len  int
        Cap  int
}

When you are copying the elements from one map to another you are not doing a deep copy of the slices (you are just creating a new struct with the same Data,Len,Cap), that is so say fetch["foo"].Data == fetchlocal["foo"].Data.

Therefore you can say fetch[someExistingKey] = someNewValue and this will not race with threadfunc, but if you say fetch[someExistingKey][x] == foobar or fetchlocal[someExistingKey][x] == foobar the race will be on.

If fetchlocal needs to mutated by threadfunc you could change your inner loop to look like:

for key, value := range fetch {
    if condition {
        newVal := make([]int, len(value))
        copy(newVal, val)
        fetchlocal[key] = newVal
    }
}

Or alternatively, do the copy inside threadfunc as needed before mutating.

P.S. If you shared your actual threadfunc or code that is modifying fetch while these two loops are running, we will be able to help more.

huangapple
  • 本文由 发表于 2014年4月10日 20:01:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/22987426.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定