Lately, I found some code looks like this:
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()
}
This program runs fine, but I think the writem
function body may be reordered by moving m = tmpm
before the for loop, because this doesn't change the behavior within this goroutine. And this reordering will cause the concurrent map read and map write
problem. As the Go Memory Model says:
compilers and processors may reorder the reads and writes executed within a single goroutine only when the reordering does not change the behavior within that goroutine as defined by the language specification
Any I right, or it is safe to write code like this?
This program runs fine.
No, it doesn't.
The results of the program are undefined. You have a data race on 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()
}
Playground: https://play.golang.org/p/OcWmK7ioMkD
Reference: Go: Data Race Detector