-1

最近,我发现一些代码看起来像这样:

var m map[int]int

func writem() {
    tmpm := make(map[int]int)
    for i := 0; i < 4000000; i++ {
        tmpm[i] = i + 10
    }
    m = tmpm
}

func readm() {
    for k, v := range m {
        _, _ = k, v
    }
}

func main() {
    writem()
    go readm()
    writem()
}

这个程序运行良好,但我认为writem函数体可以通过m = tmpm在 for 循环之前移动来重新排序,因为这不会改变这个 goroutine 中的行为。而这种重新排序将导致concurrent map read and map write问题。正如Go 内存模型所说:

只有当重新排序不会改变语言规范所定义的 goroutine 中的行为时,编译器和处理器才可以重新排序在单个 goroutine 中执行的读取和写入

我是对的,还是这样写代码是安全的?

4

1 回答 1

7

该程序运行良好。

不,它没有。


程序的结果是未定义的。您在 map 上进行了数据竞赛m

$ go run -race racer.go
==================
WARNING: DATA RACE
Write at 0x000000510fa0 by main goroutine:
  main.writem()
      /home/peter/gopath/src/racer.go:10 +0xa7
  main.main()
      /home/peter/gopath/src/racer.go:22 +0x4c

Previous read at 0x000000510fa0 by goroutine 13:
  [failed to restore the stack]

Goroutine 13 (finished) created at:
  main.main()
      /home/peter/gopath/src/racer.go:21 +0x47
==================
Found 1 data race(s)
exit status 66
$ 

racer.go

package main

var m map[int]int

func writem() {
    tmpm := make(map[int]int)
    for i := 0; i < 4000000; i++ {
        tmpm[i] = i + 10
    }
    m = tmpm
}

func readm() {
    for k, v := range m {
        _, _ = k, v
    }
}

func main() {
    writem()
    go readm()
    writem()
}

游乐场: https: //play.golang.org/p/OcWmK7ioMkD


参考:Go:数据竞争检测器

于 2019-03-09T11:54:14.877 回答