go 运行时/竞态:在使用Conn.Close进行同步时,出现了意外的竞态,

tjvv9vkg  于 3个月前  发布在  Go
关注(0)|答案(9)|浏览(122)

go版本 devel +a714470 Wed Sep 27 16:29:18 2017 +0000 linux/amd64
以下程序报告了竞争,尽管对x的读取不能与对x的赋值并行进行,因为我们等待连接关闭后再读取它。
https://play.golang.org/p/ufvoScCbSm

==================
WARNING: DATA RACE
Read at 0x00c42008a220 by main goroutine:
  main.main()
      /home/rog/src/tst.go:24 +0x288

Previous write at 0x00c42008a220 by goroutine 7:
  main.main.func1()
      /home/rog/src/tst.go:16 +0x7a

Goroutine 7 (finished) created at:
  main.main()
      /home/rog/src/tst.go:14 +0x16d
==================
2017/10/04 16:48:53 1
Found 1 data race(s)
exit status 66
vnzz0bqm

vnzz0bqm1#

如果Dial和Read因为某种原因返回错误,那么在写入之前会有一个读取操作。

2q5ifsrm

2q5ifsrm2#

这并不适用于Dial——如果Dial返回错误,我们会退出,但我添加了一些额外的错误检查以使代码更清晰。我修复了上面链接指向新代码的问题。

rfbsl7qr

rfbsl7qr3#

我认为你可能对比赛检测器要求过高了。两个不同的网络连接之间没有明显的原因关联。我的意思是,当然有关联,但只有当你知道创建一个的 Dial 与创建另一个的 Accept 相匹配时。只有当你知道这一点时,你才能看到一个的 Close 影响另一个的 Read
CC @dvyukov

x6yk4ghg

x6yk4ghg4#

@ianlancetaylor这是否适用于通过网络连接的任何因果关系(例如,使用httptest.Server并依赖于在HTTP请求回复后能够读取变量)?

3qpi33ja

3qpi33ja5#

据我所知,是的。

vaj7vani

vaj7vani6#

有趣的是,这并不是我之前考虑过的事情。我怀疑我们有数百个可能带有种族标签的测试与此问题有关。

cnwbcb6i

cnwbcb6i7#

我仍然看到两个不同的问题。

  1. 鉴于没有同步,编译器可以将x++移动到c.Close()下方,并将其视为等效。
  2. 您无法保证在第30行读取的值。在第20行执行的增量可能不会发布给其他goroutine。
    除非我对事物如何工作的理解不准确。
qni6mghb

qni6mghb8#

由于c.Close()将调用内核,我认为任何合理的Go编译器都无法将x++移动到调用之外。同样,我认为x++必须被发布。我认为任何Go实现都必须假设内核可能会创建一个happens-before关系。这是事实,虽然这不在内存规范中,但总的来说,Go并不试图欺骗程序员。

jljoyd4f

jljoyd4f9#

最终,任何系统调用都可以与任何其他系统调用同步(例如,创建一个文件,另一个进程注意到目录变得非空并向我们发送信号,因此现在文件的创建与信号的到来在某种程度上是同步的)。但是,将所有事物与所有事物同步会产生实质性的负面影响,即掩盖真正的数据竞争。
在C++中,我们进行更详细的逻辑处理,例如实际上跟踪由管道返回的一对fd之间的连接,然后如何通过dup传播:
http://llvm.org/viewvc/llvm-project/compiler-rt/trunk/lib/tsan/rtl/tsan_fd.cc?revision=308929&view=markup
但我们仍然不跟踪套接字之间的关系,理论上应该可以通过查看本地/对等地址来跟踪进程内的连接,但这仍然无法解决当连接经过单独的进程/机器时的问题。
总之:我不认为Go有简单的方法可以解决这个问题。如果我们谈论测试,我建议添加一个单独的通道,用于通知请求/连接处理完成。

相关问题