【问题标题】:Why race condition with goroutine won't happen some time?为什么 goroutine 的竞争条件不会在一段时间内发生?
【发布时间】: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。

  1. 为什么我得到 4 和 3?是因为比赛条件没有发生吗?

  2. 为什么去游乐场总是得到 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


【解决方案1】:

根据定义,竞争条件是不确定的。这意味着虽然您可能会在大多数情况下得到特定的答案,但并非总是如此。

通过在多个内核上运行活泼的代码,您大大增加了可能性的数量,因此您可以获得更广泛的结果选择。

有关竞争条件的更多信息,请参阅 this postthis Wikipedia article

【讨论】:

  • 单线程只能运行在一个核心上,对吧?使用多线程时,它会更频繁地打印 4(正确的结果)。为什么是这样 ?我有关于我的问题的更新。谢谢!
  • 这是竞态条件的“乐趣”之一。没有真正的方法可以说明会发生什么或为什么。为什么它在更多核心上更频繁地产生“正确”结果?它可能与各种功能的确切相对速度以及调度程序实现的一些细节有关。如果您更改代码或内核的数量和速度,结果可能会随机变化。
  • 嗨,如果我评论 runtime.GoSched(),它总是 4,没有竞争条件,对吧?
  • 不。比赛仍然存在,只是不太可能发生。为了防止数据竞争,您需要像互斥锁或通道这样的显式同步。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-11-04
  • 1970-01-01
  • 2019-08-20
  • 2019-12-29
  • 1970-01-01
  • 1970-01-01
  • 2021-08-27
相关资源
最近更新 更多