最近,我发现一些代码看起来像这样:
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中执行的读写进行重新排序
我正确吗,或者编写这样的代码是安全的?
答案 0 :(得分: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
$
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