This is an example from Go doc for "The Go Memory Model"
Code:
var a string
var done bool
func setup() {
a = "hello, world"
done = true
}
func main() {
go setup()
for !done {
}
print(a)
}
The origin sentence from the doc:
As before, there is no guarantee that, in main, observing the write to done implies observing the write to a, so this program could print an empty string too. Worse, there is no guarantee that the write to done will ever be observed by main, since there are no synchronization events between the two threads. The loop in main is not guaranteed to finish.
Why?
I guess the main goroutine just cache done=false variable in register and no any sync action to flush the register cache.
Thanks.
Edit 2019-08-16
I figure it out.
If runtime.GOMAXPROCS(1)
is set, the only thread will always run for !done{}
. No any chance for scheduler to switch thread context to setup()
subgoroutine. So compiler cannot promise this example to finish.
When we call build-in lib function, runtime lib will will get chance to dispatch goroutine. eg:
mutex.Lock()
// 1、enter go build-in lib
// 2、switch to other goroutine if sync condition is not satisfied
// 3、condition is satisfied
someAction()
mutex.Unlock()
When you run a function with the go keyword it opens a new goroutine, the compiler looks at this goroutine and says:
'hey, the done being set to true and the a being set to hello world are completely unrelated to each other in this goroutine so I can order them however is most efficient to me'
The compiler doesn't look at other goroutines unless there is a specific link between them, ie. a channel or a lock.
That's why it might never print a, because done might be set to true first so the for loop exits and crosses the print line before the other goroutine sets a.
You have a data race. The results are undefined.
$ go run -race racer.go
==================
WARNING: DATA RACE
Write at 0x00000052b581 by goroutine 6:
main.setup()
/home/peter/gopath/src/racer.go:8 +0x70
Previous read at 0x00000052b581 by main goroutine:
main.main()
/home/peter/gopath/src/racer.go:13 +0x56
Goroutine 6 (running) created at:
main.main()
/home/peter/gopath/src/racer.go:12 +0x46
==================
==================
WARNING: DATA RACE
Read at 0x000000510220 by main goroutine:
main.main()
/home/peter/gopath/src/racer.go:15 +0x74
Previous write at 0x000000510220 by goroutine 6:
main.setup()
/home/peter/gopath/src/racer.go:7 +0x3e
Goroutine 6 (finished) created at:
main.main()
/home/peter/gopath/src/racer.go:12 +0x46
==================
hello, worldFound 2 data race(s)
exit status 66
$
racer.go
:
package main
var a string
var done bool
func setup() {
a = "hello, world"
done = true
}
func main() {
go setup()
for !done {
}
print(a)
}
done = true
and !done
without synchronization is a data race.
a = "hello, world"
and print(a)
without synchronization is a data race.