我有一个如下所示的程序。它启动NumberOfCPUs-1goroutines 并且在每个 goroutine 中只更新全局变量x。输出是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)
}
Run Code Online (Sandbox Code Playgroud)
如果我稍微改变一下程序,就像这样:
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)
}
Run Code Online (Sandbox Code Playgroud)
x 将是一些随机的大值。
我认为这可能与 goroutine 调度程序有关。在第一种情况下,goroutines 的数量小于 cpu 内核的数量,因此mainfunc 可以与所有现有的 goroutines 一起执行。因为在每个 goroutine 内部都没有发生系统调用、I/O 或通道通信,所以 goroutine 调度程序将无法工作。并且因为 goroutines 没有被中断,updatedx没有机会被写回。
而在第二种情况下,goroutines 的数量等于 cpu 内核的数量,为了让mainfunc 有机会运行,我放了一个time.Sleep(0)after update x。我认为每次 goroutine 调度器切换 goroutines 时,updatedx都会写回其原始内存位置。
有人能证实我的想法吗?有什么遗漏吗?
谢谢。
你有多个 goroutine 共享同一个变量x,读写不同步。你有数据竞争。因此,您的结果x未定义。使用选项-race来运行竞争检测器。请参阅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)
}
Run Code Online (Sandbox Code Playgroud)
输出:
$ 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
Run Code Online (Sandbox Code Playgroud)
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)
}
Run Code Online (Sandbox Code Playgroud)
输出:
$ 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
Run Code Online (Sandbox Code Playgroud)