没有锁定同时读取函数指针是否安全?

nic*_*bot 6 concurrency go goroutine

假设我有这个:

go func() {
    for range time.Tick(1 * time.Millisecond) {
        a, b = b, a
    }
}()
Run Code Online (Sandbox Code Playgroud)

其他地方:

i := a // <-- Is this safe?
Run Code Online (Sandbox Code Playgroud)

对于这个问题,i与原始a或者相关的价值是不重要的b.唯一的问题是阅读a是否安全.也就是说,是否有可能anil部分分配,无效,未定义,......除了有效值之外的任何东西?

我试图让它失败,但到目前为止它总是成功(在我的Mac上).

Go Go Memory Model文档中,我无法找到超出此引用的任何特定内容:

大于单个机器字的值的读取和写入表现为以未指定的顺序进行的多个机器字大小的操作.

这是否意味着单个机器字写入实际上是原子的?并且,如果是这样,函数指针写入Go单个机器字操作?

更新:这是一个正确同步的解决方案

icz*_*cza 15

从多个够程,其中它们中的至少一个是写入任何变量不同步,并发访问是未定义的行为通过将GO记忆模型.

未定义意味着它所说的:未定义.可能是您的程序正常工作,可能会无法正常工作.它可能导致Go运行时提供的内存和类型安全性丢失(参见下面的示例).它甚至可能会使您的程序崩溃.或者它甚至可能导致地球爆炸(概率极小,甚至可能低于1e-40,但仍然......).

这种不确定你的情况意味着,是的,i可以nil,部分转让的,无效的,不确定的,......比任何其他东西ab.该列表只是所有可能结果的一小部分.

不要以为某些数据竞赛是(或可能是)良性或无害的.如果无人看管,它们可能是最糟糕的事情的根源.

由于您的代码a在一个goroutine中写入变量并在另一个goroutine中读取它(它试图将其值分配给另一个变量i),因此它是一个数据竞争,因此它不安全.如果在您的测试中它"正确"工作并不重要.人们可以将您的代码作为起点,扩展/构建它,并由于您最初的"无害"数据竞争而导致灾难.

作为相关问题,请阅读Golang如何安全地进行并发读/写操作?go lang中的同步不正确.

强烈建议阅读Dmitry Vyukov的博客文章:良性数据竞赛:可能出现什么问题?

这也是一篇非常有趣的博客文章,其中展示了一个以故意数据竞争打破Go的内存安全性的例子:Golang数据竞争打破内存安全

  • 通常,非常好.我还建议OP阅读[this piece](http://preshing.com/20130618/atomic-vs-non-atomic-operations/),以便更好地掌握潜在的H/W问题.总结一下OP,在文本编辑器中编写代码时,代码"下面"存在两个问题:1)编译器可以生成机器代码,它对变量的内存位置做"奇怪"的事情; 2)多CPU和/或核心硬件有缓存一致性问题:当CPU从内存中读取一个值时,它不一定是从另一个CPU写入的相同位置读取它. (2认同)
  • 哦,第三个问题:3)广泛使用的H/W平台上的现代CPU通常执行存储器访问的重新排序.所有这三个问题只有在程序员对语言内存模型所保证的期望有所期望时才会"破解",所以请不要使用它们.;-) (2认同)
  • @nicerobot 这就是我的观点。你_没有_保证`i`的值是有效的,这就是_undefined_的意思。你的程序甚至可能_crash_,我猜你不会认为这是良性的。_可能_是使用当前的编译器、硬件和生成的代码,您不会遇到这种情况,但您没有保证。甚至可能下一个版本的 Go 编译器将生成不同的“优化”代码,该代码将根据您希望程序执行的操作而“行为不端”——仅仅因为您在代码中留下了数据竞争。 (2认同)

Yan*_*ozo 5

Race condition 而言,这是不安全的。简而言之,我对竞争条件的理解是,当有多个异步例程(协程、线程、进程、goroutines 等)试图访问相同的资源并且至少一个是写入操作时,因此在您的示例中,我们有 2 goroutines 读取和写入函数类型的变量,我认为从并发的角度来看,重要的是这些变量在某处有一个内存空间,我们试图在该部分内存中读取或写入。

简短回答:只需使用带有 或的-race标志运行您的示例,您就会看到检测到的数据竞争go run -racego build -race

  • _唯一的问题是分配给 i 是否安全_ (2认同)