【发布时间】:2017-12-29 04:50:12
【问题描述】:
我正在阅读实际操作。这个例子来自chapter6/listing09.go。
// This sample program demonstrates how to create race
// conditions in our programs. We don't want to do this.
package main
import (
"fmt"
"runtime"
"sync"
)
var (
// counter is a variable incremented by all goroutines.
counter int
// wg is used to wait for the program to finish.
wg sync.WaitGroup
)
// main is the entry point for all Go programs.
func main() {
// Add a count of two, one for each goroutine.
wg.Add(2)
// Create two goroutines.
go incCounter(1)
go incCounter(2)
// Wait for the goroutines to finish.
wg.Wait()
fmt.Println("Final Counter:", counter)
}
// incCounter increments the package level counter variable.
func incCounter(id int) {
// Schedule the call to Done to tell main we are done.
defer wg.Done()
for count := 0; count < 2; count++ {
// Capture the value of Counter.
value := counter
// Yield the thread and be placed back in queue.
runtime.Gosched()
// Increment our local value of Counter.
value++
// Store the value back into Counter.
counter = value
}
}
如果您在 play.golang.org 中运行此代码,它将是 2,与本书相同。 但我的 mac 大部分时间打印 4,有时打印 2,有时甚至打印 3。
$ go run listing09.go
Final Counter: 2
$ go run listing09.go
Final Counter: 4
$ go run listing09.go
Final Counter: 4
$ go run listing09.go
Final Counter: 4
$ go run listing09.go
Final Counter: 4
$ go run listing09.go
Final Counter: 2
$ go run listing09.go
Final Counter: 4
$ go run listing09.go
Final Counter: 2
$ go run listing09.go
Final Counter: 3
系统信息 go 版本 go1.8.1 darwin/amd64 macOS 山脉 Macbook Pro
书上的解释(p140)
每个 goroutine 都会覆盖另一个 goroutine 的工作。这发生在 goroutine 交换发生时。每个 goroutine 都制作自己的 counter 变量副本,然后换出另一个 goroutine。当 goroutine 有时间再次执行时,counter 变量的值已经改变,但是 goroutine 不会更新它的副本。相反,它继续增加它拥有的副本并将值设置回计数器变量,替换其他 goroutine 执行的工作。
根据这个解释,这段代码应该总是打印 2。
为什么我得到 4 和 3?是因为比赛条件没有发生吗?
为什么去游乐场总是得到 2 个?
更新:
在我设置runtime.GOMAXPROCS(1) 后,它开始打印 2,no 4,some 3。
我猜 play.golang.org 被配置为有一个逻辑处理器。
正确的结果 4 没有竞争条件。一个逻辑处理器意味着一个线程。默认情况下,GO 具有与物理内核相同的逻辑处理器。所以, 为什么一个线程(一个逻辑处理器)导致竞争条件,而多个线程打印正确答案?
我们可以说书中的解释是错误的,因为我们也得到了 3 和 4 吗? 它如何得到 3 ? 4 是正确的。
【问题讨论】:
-
Milo 的回答很棒,但 Go 也可以告诉你这种比赛条件,即使它不会咬你(“比赛”总是会发生;只是有时你赢,有时你输不同的方式)。尝试将其作为
go run --race test.go运行,您将看到受比赛影响的确切代码行。 -
@RobNapier 非常感谢
标签: go race-condition goroutine