I have one go program as below. It starts NumberOfCPUs-1
goroutines and inside each goroutine just update global variable x
. Output is x = 0
.
func main() {
var x int
threads := runtime.GOMAXPROCS(0)-1
for i := 0; i < threads; i++ {
go func() {
for {
x++
}
}()
}
time.Sleep(time.Second)
fmt.Println("x =", x)
}
If I change the program a little bit, like this:
func main() {
var x int
threads := runtime.GOMAXPROCS(0)
for i := 0; i < threads; i++ {
go func() {
for {
x++
time.Sleep(0)
}
}()
}
time.Sleep(time.Second)
fmt.Println("x =", x)
}
x
will be some random big value.
I think it may related to goroutine scheduler. In first case, number of goroutines is less than number of cpu cores, so that main
func can be executed with all existing goroutines. Because no syscall, I/O, or channel communication happen inside each goroutine, goroutine scheduler won't work. And because goroutines are not interrupted, updated x
doesn't have chance to be written back.
While in second case, number of goroutines is equal to number of cpu cores, in order to let main
func have chance to run, I put a time.Sleep(0)
after update x
. I think every time goroutine scheduler switches goroutines, updated x
will be written back to its original memory place.
Could anyone confirm my thoughts? Is there anything missed?
Thanks.
You have multiple goroutines sharing the same variable x
with unsynchronized reads and writes. You have data races. Therefore, your results for x
are undefined. Use option -race
to run the race detector. See Introducing the Go Race Detector.
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
var x int
threads := runtime.GOMAXPROCS(0) - 1
for i := 0; i < threads; i++ {
go func() {
for {
x++
}
}()
}
time.Sleep(time.Second)
fmt.Println("x =", x)
}
Output:
$ go run -race race1.go
==================
WARNING: DATA RACE
Read at 0x00c420084010 by goroutine 7:
main.main.func1()
/home/peter/gopath/src/race1.go:15 +0x3b
Previous write at 0x00c420084010 by goroutine 6:
main.main.func1()
/home/peter/gopath/src/race1.go:15 +0x54
Goroutine 7 (running) created at:
main.main()
/home/peter/gopath/src/race1.go:13 +0xb6
Goroutine 6 (running) created at:
main.main()
/home/peter/gopath/src/race1.go:13 +0xb6
==================
x = 24717968
Found 1 data race(s)
exit status 66
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
var x int
threads := runtime.GOMAXPROCS(0)
for i := 0; i < threads; i++ {
go func() {
for {
x++
time.Sleep(0)
}
}()
}
time.Sleep(time.Second)
fmt.Println("x =", x)
}
Output:
$ go run -race race2.go
==================
WARNING: DATA RACE
Read at 0x00c4200140d0 by goroutine 7:
main.main.func1()
/home/peter/gopath/src/race2.go:15 +0x3b
Previous write at 0x00c4200140d0 by goroutine 6:
main.main.func1()
/home/peter/gopath/src/race2.go:15 +0x54
Goroutine 7 (running) created at:
main.main()
/home/peter/gopath/src/race2.go:13 +0xb3
Goroutine 6 (running) created at:
main.main()
/home/peter/gopath/src/race2.go:13 +0xb3
==================
==================
WARNING: DATA RACE
Read at 0x00c4200140d0 by goroutine 8:
main.main.func1()
/home/peter/gopath/src/race2.go:15 +0x3b
Previous write at 0x00c4200140d0 by goroutine 6:
main.main.func1()
/home/peter/gopath/src/race2.go:15 +0x54
Goroutine 8 (running) created at:
main.main()
/home/peter/gopath/src/race2.go:13 +0xb3
Goroutine 6 (running) created at:
main.main()
/home/peter/gopath/src/race2.go:13 +0xb3
==================
x = 14739962
Found 2 data race(s)
exit status 66