首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么这段代码会导致数据竞争?

为什么这段代码会导致数据竞争?
EN

Stack Overflow用户
提问于 2017-09-15 07:07:14
回答 2查看 1.7K关注 0票数 2
代码语言:javascript
复制
1 package main
2
3 import "time"
4
5 func main() {
6     m1 := make(map[string]int)
7     m1["hello"] = 1
8     m1["world"] = 2
9     go func() {
10         for i := 0; i < 100000000; i++ {
11             _ = m1["hello"]
12         }
13     }()
14     time.Sleep(100 * time.Millisecond)
15     m2 := make(map[string]int)
16     m2["hello"] = 3
17     m1 = m2
18 }

我使用以下代码运行命令go run --race并获得:

代码语言:javascript
复制
==================
WARNING: DATA RACE
Read at 0x00c420080000 by goroutine 5:
  runtime.mapaccess1_faststr()
      /usr/local/go/src/runtime/hashmap_fast.go:208 +0x0
  main.main.func1()
      /Users/meitu/test/go/map.go:11 +0x80

Previous write at 0x00c420080000 by main goroutine:
  runtime.mapassign()
      /usr/local/go/src/runtime/hashmap.go:485 +0x0
  main.main()
      /Users/meitu/test/go/map.go:16 +0x220

Goroutine 5 (running) created at:
  main.main()
      /Users/meitu/test/go/map.go:13 +0x1aa
==================

m1m2是不同的变量,为什么第16行和第11行导致数据竞争?

我的围棋版本是1.8。我想这是一些编译优化,有人可以告诉我吗?非常感谢。

EN

回答 2

Stack Overflow用户

发布于 2017-09-15 07:15:51

requirements 进行数据竞争是:

  1. 同时访问同一资源(例如,变量)的多个goroutines。
  2. 这些访问中至少有一个是写的。
  3. 访问的时间是刻骨铭心的。

在您的代码中,满足了所有3项要求:

  1. 您有访问m1的主goroutine,在其中开始的访问也访问m1。主goroutine在其他goroutine启动后访问它,因此它们是并行的。
  2. 主要的goroutine将m1写在第17行:m1 = m2中。
  3. 访问不是同步的,您没有使用互斥或通道之类的东西(睡眠是而不是同步)。

因此,这是一个数据竞赛。

显而易见的数据竞争是在读取m1的第11行和写m1的第17行之间。

但!由于第17行将m2分配给m1,那么当/如果已启动的goroutine继续运行时,它将尝试读取m1,这可能是m2的值,因为我们将m2分配给m1。这意味着什么?m2介绍了另一个数据竞赛--编写和mean m1**.**

这是在第17行之后,如果程序没有立即结束(它可能,但不一定),那么启动的goroutine尝试从m1 (现在是最后一次写在第16行中的m2 )读取,所以这解释了第11行和第16行之间的“冲突”。

完整的go run -race输出如下:

代码语言:javascript
复制
==================
WARNING: DATA RACE
Write at 0x00c42000e010 by main goroutine:
  main.main()
      /home/icza/gows/src/play/play2.go:17 +0x22f

Previous read at 0x00c42000e010 by goroutine 5:
  [failed to restore the stack]

Goroutine 5 (running) created at:
  main.main()
      /home/icza/gows/src/play/play2.go:9 +0x190
==================
==================
WARNING: DATA RACE
Read at 0x00c42007e000 by goroutine 5:
  runtime.mapaccess1_faststr()
      /usr/local/go/src/runtime/hashmap_fast.go:208 +0x0
  main.main.func1()
      /home/icza/gows/src/play/play2.go:11 +0x7a

Previous write at 0x00c42007e000 by main goroutine:
  runtime.mapassign_faststr()
      /usr/local/go/src/runtime/hashmap_fast.go:598 +0x0
  main.main()
      /home/icza/gows/src/play/play2.go:16 +0x1fc

Goroutine 5 (running) created at:
  main.main()
      /home/icza/gows/src/play/play2.go:9 +0x190
==================
==================
WARNING: DATA RACE
Read at 0x00c420080088 by goroutine 5:
  main.main.func1()
      /home/icza/gows/src/play/play2.go:11 +0x90

Previous write at 0x00c420080088 by main goroutine:
  main.main()
      /home/icza/gows/src/play/play2.go:16 +0x212

Goroutine 5 (running) created at:
  main.main()
      /home/icza/gows/src/play/play2.go:9 +0x190
==================
Found 3 data race(s)
exit status 66
票数 3
EN

Stack Overflow用户

发布于 2017-09-15 07:40:31

数据竞争

当两个goroutines同时访问相同的变量并且至少一个访问是写时,就会发生数据竞争。

指令重新排序

编译器和处理器可以重新排序在单个goroutine中执行的读和写,只要重新排序不会改变例程中的行为,它将确保其他goroutines的行为不受影响。

代码语言:javascript
复制
m2["hello"] = 3
m1 = m2

可以重新排序

代码语言:javascript
复制
m1 = m2
m2["hello"] = 3

这不会改变主例程的行为,因此,竞赛检查也会考虑这一点,以评估比赛状况。现在我们有了引起竞赛条件的m2["hello"] = 3,它打印出了与其原始行号相同的结果。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/46233680

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档