Threading.Volatile.Read(Int64) 和 Threading.Interlocked.Read(Int64) 之间的区别?

问题描述 投票:0回答:2

.NET 系统类

Read(Int64)
System.Threading.Volatile
System.Threading.Interlocked
方法有什么区别(如果有)?

具体而言,它们在 (a) 原子性和 (b) 内存排序方面各自的保证/行为是什么。

请注意,这是关于

Volatile
类,而不是 volatile
(小写)关键字。


MS 文档指出:

Volatile.Read 方法

读取字段的值。在需要它的系统上,插入一个 阻止处理器重新排序内存的内存屏障 操作如下:如果在该方法之后出现读或写 代码,处理器无法在此方法之前移动它。

...

退货

Int64


  
  

读取的值。该值是任何处理器最新写入的值 在计算机中,无论处理器的数量或状态如何 处理器缓存。

Interlocked.Read(Int64) 方法

返回 64 位值,作为原子操作加载。

特别令人困惑的是,

Volatile

文档没有谈论原子性,而
Interlocked
文档没有谈论排序/内存障碍。 

旁注:仅供参考:我更熟悉

C++ 原子 API,其中原子操作始终还指定内存排序语义。


Pavel

提供的问题链接(和传递性链接)很好地解释了内存屏障中的易失性和无撕裂读取中的原子性的差异/正交性,但他们没有解释这两个概念如何应用于这两个类。

    Volatile.Read
  • 对原子性有任何保证吗?
  • Interlocked.Read(或者实际上任何
    Interlocked
    函数)对内存顺序有任何保证吗?
    
    
c# multithreading volatile interlocked
2个回答
6
投票
Interlocked.Read

转换为 CompareExchange 操作
public static long Read(ref long location) { return Interlocked.CompareExchange(ref location, 0, 0); }

因此它具有 CompareExchange 的所有优点:

全内存屏障
  • 原子性
  • 另一方面,
Volatile.Read

仅获得语义。它可以帮助您确保读取操作的执行顺序,而无需任何原子性或新鲜度保证。

    


2
投票

Volatile.Read(long)

方法的文档没有提及任何有关原子性的内容,但所有
Volatile操作的原子性在
Volatile
类的文档中明确保证

Volatile

类还提供一些64位类型的读写操作,例如

Int64
Double
。即使在 32 位处理器上,此类 64 位内存上的易失性读取和写入也是原子的,这与常规读取和写入不同。

此外,
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 通常仅提供半栅栏,足以获取获取/释放语义(
示例
)。

注意:

Intrinsic

属性意味着修饰方法的代码可能会被 Jitter 替换/优化。如果文档中没有明确保证原子性,这可能会令人担忧。

© www.soinside.com 2019 - 2024. All rights reserved.