【问题标题】:May the go compiler reorder following code?go 编译器可以重新排序以下代码吗?
【发布时间】:2019-03-09 10:50:49
【问题描述】:

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

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 函数体可以通过在 for 循环之前移动 m = tmpm 来重新排序,因为这不会改变这个 goroutine 中的行为。而这种重新排序将导致concurrent map read and map write 问题。正如Go Memory Model 所说:

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

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

【问题讨论】:

  • 您引用的那句话不适用于您的情况,因为读取和写入 not 在同一个 goroutine 中。所以我不太明白你的问题。

标签: go concurrency happens-before data-race


【解决方案1】:

这个程序运行良好。

不,它没有。


程序的结果是未定义的。您在地图 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: Data Race Detector

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-01-16
    • 2010-10-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多