[英]Difference between Threading.Volatile.Read(Int64) and Threading.Interlocked.Read(Int64)?
.NET 系统类System.Threading.Volatile
和System.Threading.Interlocked
的Read(Int64)
方法有什么区别(如果有)?
具体来说,关于(a)原子性和(b)内存排序,它们各自的保证/行为是什么。
请注意,这是关于Volatile
类,而不是volatile
(小写)关键字。
MS 文档状态:
Volatile.Read 方法
读取字段的值。 在需要它的系统上,插入一个内存屏障,以防止处理器重新排序内存操作,如下所示:如果在代码中此方法之后出现读取或写入,则处理器无法在此方法之前移动它。
...
返回
Int64
读取的值。 该值是计算机中任何处理器写入的最新值,与处理器数量或处理器缓存状态无关。
对比
Interlocked.Read(Int64) 方法
返回一个 64 位值,作为原子操作加载。
特别令人困惑的是, Volatile
文档没有谈论原子性,而Interlocked
文档没有谈论排序/内存障碍。
旁注:作为参考:我更熟悉C++ 原子 API ,其中原子操作也总是指定内存排序语义。
Pavel提供的问题链接(和传递链接)很好地解释了 volatile-as-in-memory-barrier 和 atomic-as-in-no-torn-reads 的差异/正交性,但它们没有解释这两个概念如何应用于这两个类。
Volatile.Read
是否对原子性做出任何保证?Interlocked.Read
(或者,实际上,任何Interlocked
函数)是否对内存顺序做出任何保证? Interlocked.Read
转换为 CompareExchange 操作:
public static long Read(ref long location)
{
return Interlocked.CompareExchange(ref location, 0, 0);
}
因此,它具有 CompareExchange 的所有优点:
另一方面, Volatile.Read
仅获取语义。 它可以帮助您确保读取操作的执行顺序,而无需任何原子性或新鲜度保证。
Volatile.Read(long)
方法的文档没有提到任何关于原子性的内容,但源代码非常有启发性:
private struct VolatileIntPtr { public volatile IntPtr Value; }
[Intrinsic]
[NonVersionable]
public static long Read(ref long location) =>
#if TARGET_64BIT
(long)Unsafe.As<long, VolatileIntPtr>(ref location).Value;
#else
// On 32-bit machines, we use Interlocked, since an ordinary volatile read would not be atomic.
Interlocked.CompareExchange(ref location, 0, 0);
#endif
Volatile.Read
方法间接调用Interlocked.CompareExchange
,就像Interlocked.Read
一样( 源代码),因此两者之间没有区别。 两种方法都会发出一个完整的栅栏。 所以Volatile.Read
似乎是整体上更可取的选择。 尽管文档不保证它的原子性,但如果它不是原子的,它的有用性将受到严重限制(如果有的话)。 您对可能被破坏的价值有什么用处?
注意: Intrinsic
属性意味着被修饰方法的代码可能被 Jitter 替换/优化。 这可能有点令人担忧,因此请自行判断在多线程环境中使用Volatile.Read
读取long
值是否安全。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.