1 package main
2
3 import "time"
4
5 func main() {
6 m1 := make(map[string]int)
7 m1["hello"] = 1
8 m1["world"] = 2
9 go func() {
10 for i := 0; i < 100000000; i++ {
11 _ = m1["hello"]
12 }
13 }()
14 time.Sleep(100 * time.Millisecond)
15 m2 := make(map[string]int)
16 m2["hello"] = 3
17 m1 = m2
18 }
I run command go run --race
with this code and get:
==================
WARNING: DATA RACE
Read at 0x00c420080000 by goroutine 5:
runtime.mapaccess1_faststr()
/usr/local/go/src/runtime/hashmap_fast.go:208 +0x0
main.main.func1()
/Users/meitu/test/go/map.go:11 +0x80
Previous write at 0x00c420080000 by main goroutine:
runtime.mapassign()
/usr/local/go/src/runtime/hashmap.go:485 +0x0
main.main()
/Users/meitu/test/go/map.go:16 +0x220
Goroutine 5 (running) created at:
main.main()
/Users/meitu/test/go/map.go:13 +0x1aa
==================
m1
and m2
are different variables,why do line 16 and line 11 cause data race?
My go version is 1.8. I guess that is some compile optimization, and somebody can tell me about it? Thank you very much.
The requirements to have a data race are:
- Multiple goroutines accessing the same resource (e.g. a variable) concurrently.
- At least one of those accesses is a write.
- The accesses are uncynchronized.
In your code all 3 requirements are met:
- You have the main goroutine accesssing
m1
, and the one you start in it also accesses m1
. The main goroutine accesses it after the other goroutine has been launched, so they are concurrent.
- The main goroutine writes
m1
in line #17: m1 = m2
.
- The accesses are not synchronized, you use no mutex or channels or anything like that (sleeping is not synchronization).
Therefore it's a data race.
The obvious data race is between lines #11 reading m1
, and line #17 writing m1
.
But! Since line #17 assigns m2
to m1
, then when/if the launched goroutine continues to run, it attempts to read m1
which may now be the value of m2
because we assigned m2
to m1
. What does this mean? This introduces another data race writing m2
and reading m1
.
That is after line #17 if the program does not end immediately (it may, but not necessarily), then the launched goroutine attempts to read from m1
which is now m2
which was last written in line #16, so this explains the "conflict" between lines #11 and #16.
The full go run -race
output is as follows:
==================
WARNING: DATA RACE
Write at 0x00c42000e010 by main goroutine:
main.main()
/home/icza/gows/src/play/play2.go:17 +0x22f
Previous read at 0x00c42000e010 by goroutine 5:
[failed to restore the stack]
Goroutine 5 (running) created at:
main.main()
/home/icza/gows/src/play/play2.go:9 +0x190
==================
==================
WARNING: DATA RACE
Read at 0x00c42007e000 by goroutine 5:
runtime.mapaccess1_faststr()
/usr/local/go/src/runtime/hashmap_fast.go:208 +0x0
main.main.func1()
/home/icza/gows/src/play/play2.go:11 +0x7a
Previous write at 0x00c42007e000 by main goroutine:
runtime.mapassign_faststr()
/usr/local/go/src/runtime/hashmap_fast.go:598 +0x0
main.main()
/home/icza/gows/src/play/play2.go:16 +0x1fc
Goroutine 5 (running) created at:
main.main()
/home/icza/gows/src/play/play2.go:9 +0x190
==================
==================
WARNING: DATA RACE
Read at 0x00c420080088 by goroutine 5:
main.main.func1()
/home/icza/gows/src/play/play2.go:11 +0x90
Previous write at 0x00c420080088 by main goroutine:
main.main()
/home/icza/gows/src/play/play2.go:16 +0x212
Goroutine 5 (running) created at:
main.main()
/home/icza/gows/src/play/play2.go:9 +0x190
==================
Found 3 data race(s)
exit status 66
Data race
A data race occurs when two goroutines access the same variable concurrently and at least one of the accesses is a write.
Instructions Reorder
compilers and processors may reorder the reads and writes executed within a single goroutine as far as the reordering does not change the behaviour within the routine, it doesn' ensure behaviour of other goroutines to be unaffected
m2["hello"] = 3
m1 = m2
Can be reordered to
m1 = m2
m2["hello"] = 3
That won't alter the behaviour of the main routine and thus race checking will consider that also for evaluating race condition. Now we have m2["hello"] = 3
causing the race condition and it prints out the same with its original line number