英文:
Different behavior of updating global variable in goroutine
问题
我有一个如下的Go程序。它启动NumberOfCPUs-1
个goroutine,在每个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)
}
如果我稍微修改一下程序,像这样:
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
的值将会是一个随机的大数。
我认为这可能与goroutine调度器有关。在第一种情况下,goroutine的数量少于CPU核心的数量,因此main
函数可以在所有现有的goroutine中执行。因为没有系统调用、I/O或通道通信发生在每个goroutine内部,所以goroutine调度器不会工作。而且由于goroutine没有被中断,更新后的x
没有机会被写回。
而在第二种情况下,goroutine的数量等于CPU核心的数量,为了让main
函数有机会运行,我在更新x
之后加了一个time.Sleep(0)
。我认为每次goroutine调度器切换goroutine时,更新后的x
将被写回到它的原始内存位置。
有人可以确认我的想法吗?有什么遗漏的地方吗?
谢谢。
英文:
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.
答案1
得分: 6
你有多个goroutine共享同一个变量x
,并且存在未同步的读写操作。这会导致数据竞争,因此x
的结果是不确定的。你可以使用-race
选项来运行竞争检测器。详见《Go竞争检测器介绍》。
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)
}
输出:
$ 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)
}
输出:
$ 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
英文:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论