.NET 系统类
Read(Int64)
和
System.Threading.Volatile
的
System.Threading.Interlocked
方法有什么区别(如果有)?
具体而言,它们在 (a) 原子性和 (b) 内存排序方面各自的保证/行为是什么。
请注意,这是关于
Volatile
类,而不是 volatile
(小写)关键字。
Volatile.Read 方法与读取字段的值。在需要它的系统上,插入一个 阻止处理器重新排序内存的内存屏障 操作如下:如果在该方法之后出现读或写 代码,处理器无法在此方法之前移动它。
...
退货
读取的值。该值是任何处理器最新写入的值 在计算机中,无论处理器的数量或状态如何 处理器缓存。
Int64
Interlocked.Read(Int64) 方法特别令人困惑的是,返回 64 位值,作为原子操作加载。
Volatile
文档没有谈论原子性,而
Interlocked
文档没有谈论排序/内存障碍。旁注:仅供参考:我更熟悉
C++ 原子 API,其中原子操作始终还指定内存排序语义。
提供的问题链接(和传递性链接)很好地解释了内存屏障中的易失性和无撕裂读取中的原子性的差异/正交性,但他们没有解释这两个概念如何应用于这两个类。
Volatile.Read
Interlocked.Read
(或者实际上任何Interlocked
函数)对内存顺序有任何保证吗?
方法的文档没有提及任何有关原子性的内容,但所有
Volatile
操作的原子性在Volatile
类的文档中明确保证:
Volatile
类还提供一些64位类型的读写操作,例如
和Int64
。即使在 32 位处理器上,此类 64 位内存上的易失性读取和写入也是原子的,这与常规读取和写入不同。 此外,Double
Volatile.Read(long)
的源代码也揭示了:
private struct VolatileIntPtr { public volatile IntPtr Value; }
[Intrinsic]
[NonVersionable]
public static long Read(ref readonly long location) =>
#if TARGET_64BIT
(long)Unsafe.As<long, VolatileIntPtr>(ref Unsafe.AsRef(in location)).Value;
#else
// On 32-bit machines, we use Interlocked, since an ordinary volatile read
// would not be atomic.
Interlocked.CompareExchange(ref Unsafe.AsRef(in location), 0, 0);
#endif
在32位机器上,Volatile.Read
Interlocked.CompareExchange
,就像Interlocked.Read
一样(源代码),所以两者之间没有区别。两种方法都会发出完整的栅栏。 在 64 位机器上,读取的原子性由 CPU 架构保证,因此会发出更便宜的半栅栏。
Volatile.Read
应该是更好的选择,除非您需要
Interlocked
API 提供的完整内存屏障。 Volatile
API 通常仅提供半栅栏,足以获取获取/释放语义(示例)。 注意:
属性意味着修饰方法的代码可能会被 Jitter 替换/优化。如果文档中没有明确保证原子性,这可能会令人担忧。