Volatile.Read / Volatile.Write 对于 "double" 类型的变量是原子操作吗?

10

MSDN指出:

对于其他类型的读写操作,包括longulongdoubledecimal,以及用户定义的类型,不一定是原子操作。

C# 7.0草案规范 - 变量 - 9.6 变量引用的原子性

Volatile.Write(Double, Double)能作为原子操作吗?如果可以,这是如何保证的?

在一个线程中执行Volatile.Write(ref mydouble, value);,在另一个线程中执行Volatile.Read(ref mydouble),其中mydouble的类型是double,这样做是否安全?

这是一个一般性问题。另一个问题是,在这种特定情况下我应该怎么做:

  • 2个Xeon x64服务器处理器
  • Windows + MS .NET 4.5
  • 从不同的线程中进行读写操作
  • 最小的延迟(需要最小的延迟,因为我正在编写高频交易软件)
2个回答

7
不,Volatile不是原子性的,在SMP(>1处理器)系统中假设它是安全的是不安全的。在单处理器机器上是安全的。
除非你真的需要性能,否则你可能想使用Interlocked,可以使用Interlocked.Exchange或Interlocked.Read。

2
@javapowered,就我个人而言,我不知道“volatile”(对于double)是否意味着原子性的任何保证。因此,如果我正在进行无锁访问,我个人将使用“Interlocked”。实际上,即使在x64上,也要使用Interlocked,其中(作为芯片大小的副作用)它几乎肯定是原子的。因此,我必须同意“使用Interlocked”是问题的最合适答案。 - Marc Gravell
2
@MarcGravell 什么时候应该使用“Volatile”类?如果它变得无用,那就总是使用“Interlocked”吗? - Oleg Vazhnev
1
如果你引发了一个额外的页面错误,那么它会抵消成千上万次Volatile调用所带来的收益。页面错误和优化内存局部性(即数组与列表)将为您带来更多的性能优势。阅读一些XNA人员推荐的优化内容,他们有很多关于此方面的好东西。 - Ana Betts
1
@MarcGravell 我认为人们普遍误解了 MSDN 文档中的一句话,认为 Interlocked 对于大于 32 位的值不是原子性的。然而,这完全取决于它运行的架构。如果在 x64 上运行,则 Interlocked 对于 64 位值类型是原子性的。我很大程度上怀疑 volatile 也是如此(即在 x64 系统上对于 double/long/8 字节值类型是原子性的)。更多信息请参见 http://www.albahari.com/threading/part4.aspx。顺便说一句,我通过查看调用的 API 得出了这个结论,我可以百分之百确定 Interlocked 在 x64 上对于 8 字节值是原子性的。 - M Afifi
1
@PaulBetts,你有关于XNA推荐优化的好链接吗? - M Afifi
显示剩余8条评论

1
截至2023年,Volatile的文档(和/或实现)已经更新,现在声明如下:

Volatile类还为一些64位类型(如Int64和Double)提供读写操作。在32位处理器上,即使是普通的读写操作,对这些64位内存的读写也是原子的。

因此,对64位值进行Volatile.ReadVolatile.Write操作实际上是原子的,即使在32位系统上
然而,请注意使用Volatile类进行的读写操作与使用volatile字段进行的读写操作并不相同。因此,访问64位的volatile字段不是原子操作,而使用Volatile类则是原子操作。
根据volatile(关键字)文档中的说明:

其他类型,包括double和long,不能被标记为volatile,因为对这些类型的字段的读写操作不能保证是原子操作的。为了保护对这些类型字段的多线程访问,可以使用Interlocked类的成员或使用lock语句进行访问保护。

有趣的是,这里并没有提到Volatile类用于原子访问,我个人认为这部分文档有点过时。

所以访问64位的volatile字段是不原子的。你甚至不能将64位字段标记为volatile。C#编译器不允许这样做。所以对于64位volatile字段的特性做出的陈述就像对独角兽生物学做出的陈述一样毫无意义! - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接