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中执行的读写进行重新排序。
是的,还是这样编写代码安全吗?
该程序运行良好。
不,不是。
程序的结果是不确定的。您在地图上有一场数据竞赛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:数据竞速检测器