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.
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
Can be reordered to
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 numberThe requirements to have a data race are:
In your code all 3 requirements are met:
m1
, and the one you start in it also accessesm1
. The main goroutine accesses it after the other goroutine has been launched, so they are concurrent.m1
in line #17:m1 = m2
.Therefore it's a data race.
The obvious data race is between lines #11 reading
m1
, and line #17 writingm1
.But! Since line #17 assigns
m2
tom1
, then when/if the launched goroutine continues to run, it attempts to readm1
which may now be the value ofm2
because we assignedm2
tom1
. What does this mean? This introduces another data race writingm2
and readingm1
.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 nowm2
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: