Go 为什么这段代码会导致数据竞争?
Go 为什么这段代码会导致数据竞争?,go,concurrency,goroutine,data-race,Go,Concurrency,Goroutine,Data Race,m1和m2是不同的变量,为什么第16行和第11行会导致数据争用 我的围棋版本是1.8。我想这是一些编译优化,有人能告诉我吗?非常感谢。数据竞赛的要求如下: 多个goroutine同时访问同一资源(例如变量) 这些访问中至少有一个是写访问 访问是不同步的 在您的代码中,所有3项要求都得到满足: 您拥有访问m1的主goroutine,并且您在其中启动的goroutine也访问m1。主goroutine在另一个goroutine启动后访问它,因此它们是并发的 主goroutine将m1写入第17行:m
m1
和m2
是不同的变量,为什么第16行和第11行会导致数据争用
我的围棋版本是1.8。我想这是一些编译优化,有人能告诉我吗?非常感谢。数据竞赛的要求如下:
m1
的主goroutine,并且您在其中启动的goroutine也访问m1
。主goroutine在另一个goroutine启动后访问它,因此它们是并发的m1
写入第17行:m1=m2
m1
和第17行写入m1
之间
但是!由于第17行将m2
分配给m1
,因此当/如果启动的goroutine继续运行时,它会尝试读取m1
,现在可能是m2
的值,因为我们将m2
分配给m1
。这是什么意思这引入了另一种数据竞争,即写入m2
和读取m1
也就是在第17行之后,如果程序没有立即结束(可能,但不一定),那么启动的goroutine会尝试读取第16行最后写入的m1
,现在是m2
,因此这解释了第11行和第16行之间的“冲突”
完整的go run-race
输出如下:
==================
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
==================
数据竞争 当两个goroutine同时访问同一个变量,并且至少有一个访问是写入时,就会发生数据争用 指令重新排序 编译器和处理器可以对单个goroutine中执行的读写操作重新排序,只要重新排序不会改变例程中的行为,也不会确保其他goroutine的行为不受影响
==================
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
可以重新排序到
m2["hello"] = 3
m1 = m2
这不会改变主程序的行为,因此竞赛检查也将考虑到比赛条件的评估。现在我们有
m2[“hello”]=3
导致了竞争条件,它打印出了与原始行号相同的行号似乎是行号混淆了。我看到了11和17之间的竞争。@Volker实际上我的go工具将两者都打印为数据竞争,#16是第一次数据竞争的结果,如我的回答中所述。是的,第17行和第11行是数据竞争,但我不明白为什么第16行和第11行是数据竞争,它显示在“竞争检测器打印信息”中@黄英俊 这是因为,如果启动的goroutine在第17行主goroutine中的赋值之后继续运行,并读取m1
,则可能是分配的m2
,最后写入第16行。请参阅编辑后的答案。谢谢。但我还是有些困惑。将m2分配给m1是在写入m2之后,这是否意味着这种情况永远不会发生,它只是一个竞赛检测器警告@黄英俊 竞赛检测器仅显示发生事件的警告。您启动goroutine,它一直读取m1
,然后写入m2
,然后将m2
分配给m1
,因此启动的goroutine现在将读取同时修改的m2
(在goroutine启动后),因此通过m1
读取m2
也是一种数据竞争,已启动的goroutine无法读取m2到m1。按顺序执行读取m1、写入m2和将m2分配给m1。这就是我所困惑的。如果是这种情况(重新排序导致了问题),那么通过向go工具传递-gcflags“-N-l”
来禁用所有优化和内联不应该消除这种数据竞争吗?但事实并非如此。重新排序不是原因,因为无论怎样,m1=m2
都是一个潜在的数据竞争点。重新排序应使其将分配计算为m2[“hello”]=3
相当于m1[“hello”]=3
我理解这一点,但上述标志不也应禁用重新排序吗?如果应该,则不会发生重新排序,因此m2[“hello”]=3
将不等同于m1[“hello”]=3
。谢谢。你能告诉我更多的细节吗?如果重新排序导致此数据竞争,我如何消除它?我需要做一个实验来帮助我更深入地理解这些原理。@icza指令重新排序不是任何错误的原因,探究答案。试图让我们证明比赛地点的合理性
m2["hello"] = 3
m1 = m2
m1 = m2
m2["hello"] = 3