【问题标题】:Go integer data raceGo 整数数据竞赛
【发布时间】:2021-04-15 04:20:18
【问题描述】:

关于 int 类型的 Go 数据竞争的问题。 当我通过go run -race main.go 运行下面的程序时,

package main

import "fmt"

var i int

func main() {

    go func() {
        i = 10
    }()

    i = 5

    fmt.Println(i)
}

它显示数据竞争警告。

~  $ go run -race main.go
5
==================
WARNING: DATA RACE
Write at 0x000000605908 by goroutine 7:
  main.main.func1()
      /home/dty1er/main.go:80 +0x3a

Previous write at 0x000000605908 by main goroutine:
  main.main()
      /home/dty1er/main.go:83 +0x56

Goroutine 7 (running) created at:
  main.main()
      /home/dty1er/main.go:79 +0x46
==================
Found 1 data race(s)
exit status 66

我知道为什么会发生这种数据竞争; var i 被 2 个 goroutines 并发访问,没有任何同步。 我不明白的是这段代码的实际问题。据我了解,因为iint,(而我的电脑是64 位的),所以i 实际上是64 位的。在 64 位机器上,i 可以原子地读取/写入。所以我认为没有实际问题。

例如,如果类型是map或者slice,由于CPU的内存访问不能是原子的,所以它的中间状态有时是可见的,这是有问题的。

但是当我只是使用int类型值时,即使有数据竞争,看起来也没有实际问题。

我的理解正确吗?

【问题讨论】:

  • 查看旧的What could possibly go wrong? 博文。
  • 在 64 位机器上,i 可以被原子地读取/写入。 这太过简单了。您有多个内核、分支预测、编译器优化,并且可能最重要的是,该语言指定不允许这样做,仅举几个您无法以可靠结果执行此操作的原因。
  • 您也不是在机器级别编写代码,而是为 Go 编译器和运行时编写代码。如果语言规范说它无效,那么硬件做什么都没关系。如果不需要额外的指令来在你的硬件上分配整数值(注意 Go 在许多不同的架构上运行),那么 编译器 如果可以这样做。
  • 看看旧的 可能出了什么问题? blog-post 我应该早点读到这个。谢谢!
  • 语言指定这是不允许的事实我在 Go 语言规范中找不到这是不允许的。这是各种语言的共同行为吗?

标签: go concurrency goroutine


【解决方案1】:

我的理解正确吗?

不,不是。具有数据竞争的程序是未定义的,使用 int 不会神奇地使这种数据竞争消失。编译器和硬件不再像 80 年代那样简单。数据竞争是一个主要错误。

您可以使用 sync/atomic 以无竞争的方式处理整数,但在此设置中简单的 int 是完全错误的。

【讨论】:

  • 谢谢。现在我发现如果发生数据竞争,程序会出现未定义的行为,这就是问题所在。我仍然没有完全理解导致未定义行为的原因(似乎是由编译器优化或某些 cpu 优化引起的),接下来我将深入研究它。
【解决方案2】:

当您运行该代码时,您希望打印什么,5 还是 10?你永远无法确定它是什么,这就是数据竞赛。在这种情况下,Go 中的 int 是原子的这一事实与此无关。

【讨论】:

  • 你搞错了。 int 不是原子的。这段代码可以用 sync/atomic 重写,然后它会是无竞争的,但仍然不可预测。仅仅因为某些事情是不可预测的并不意味着它是一个数据竞争(例如,考虑具有多种情况的 select 语句)。
  • Go 中的 int 绝对不是原子的。它可能在硬件级别的某些架构上,但 Go 可以在许多不同的架构上运行,并且相同的语言规范必须适用于所有架构。
猜你喜欢
  • 1970-01-01
  • 2013-12-20
  • 1970-01-01
  • 1970-01-01
  • 2012-09-06
  • 1970-01-01
  • 1970-01-01
  • 2015-09-15
  • 1970-01-01
相关资源
最近更新 更多