go编译器可以重新排序以下代码吗?

lty*_*lty -1 concurrency go happens-before data-race

最近,我发现一些代码如下所示:

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()
}
Run Code Online (Sandbox Code Playgroud)

该程序运行良好,但是我认为writem可以通过m = tmpm在for循环之前移动功能主体来重新排序,因为这不会更改此goroutine中的行为。而且这种重新排序将导致concurrent map read and map write问题。正如Go Memory Model所说:

仅当重新排序不会改变语言规范所定义的该goroutine中的行为时,编译器和处理器才可以对单个goroutine中执行的读写进行重新排序。

是的,还是这样编写代码安全吗?

pet*_*rSO 7

该程序运行良好。

不,不是。


程序的结果是不确定的。您在地图上有一场数据竞赛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
$ 
Run Code Online (Sandbox Code Playgroud)

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()
}
Run Code Online (Sandbox Code Playgroud)

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


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