易失性与易失性读/写?
我找不到任何 VolatileRead / write(尝试…)的例子,但仍然:
我什么时候应该使用volatile
vs VolatileRead
?
AFAIK volatile
的全部目的是创造半围栏所以:
- 对于READ操作,在当前操作之后进行的读/写(在其他线程上)将不会在栅栏之前传递。 因此 – 我们读了最新的价值。
问题#1
那我为什么需要volatileRead
呢? 似乎volatile
已经完成了工作。
另外 – 在C#中, 所有写入都是易失性的 (与Java中不同),无论您是写入volatile
还是非易失性字段 – 所以我问:为什么我需要volatileWrite
?
问题2
这是VolatileRead
的实现:
[MethodImpl(MethodImplOptions.NoInlining)] public static int VolatileRead(ref int address) { int num = address; MemoryBarrier(); return num; }
为什么行int num = address;
在那儿? 他们已经有了明确保持价值的地址参数。
你永远不应该使用Thread.VolatileRead / Write()。 这是.NET 1.1中的一个设计错误,它使用了完整的内存屏障。 这已在.NET 2.0中得到纠正,但他们无法再修复这些方法,并且必须添加一种新方法来实现它,由System.Threading.Volatile类提供。 这是抖动所知道的类,它在jit时将方法替换为适合特定处理器类型的版本。
通过参考源提供的Volatile类的源代码中的注释告诉故事(编辑为适合):
// Methods for accessing memory with volatile semantics. These are preferred over // Thread.VolatileRead and Thread.VolatileWrite, as these are implemented more // efficiently. // // (We cannot change the implementations of Thread.VolatileRead/VolatileWrite // without breaking code that relies on their overly-strong ordering guarantees.) // // The actual implementations of these methods are typically supplied by the VM at // JIT-time, because C# does not allow us to express a volatile read/write from/to // a byref arg. See getILIntrinsicImplementationForVolatile() in jitinterface.cpp.
是的,你很难找到它的用法示例。 参考源是一个很好的指南,有兆字节的精心编写,测试和战斗伤痕累累的C#代码,处理线程。 它使用VolatileRead / Write的次数: 零 。
坦率地说,.NET内存模型混乱了由CLR mm和C#mm做出的相互矛盾的假设,最近刚为ARM内核添加了新的规则。 volatile关键字的怪异语义对于不同的体系结构意味着不同的东西,这是一些证据。 尽管对于具有弱内存模型的处理器,您通常可以假设C#语言规范所说的准确。
请注意,Joe Duffy放弃了所有的希望,并且不再劝阻所有人使用它。 假设您可以比语言和框架提供的原语做得更好,这一般是非常不明智的。 Volatile类的备注部分带来了重点:
在正常情况下,C#lock语句,Visual Basic SyncLock语句和Monitor类提供了同步访问数据的最简单且最不容易出错的方式,而Lazy类提供了一种简单的方法来编写延迟初始化代码而无需直接使用双重检查锁定。
当您需要对代码应用围栏的方式进行更细粒度的控制时,您可以使用static
Thread.VolatileRead或Thread.VolatileWrite 。
声明变量volatile意味着编译器不会缓存它的值并始终读取字段值,并且在执行写入时,编译器会立即写入指定的值。
Thread.VolatileRead和Thread.VolatileWrite这两个方法使您能够在不将变量声明为volatile的情况下进行更细粒度的控制,因为您可以决定何时执行易失性读操作以及何时执行volatile写操作,而无需读取no你声明变量挥发时立即缓存和写入,所以用差的话来说你有更多的控制权和更多的自由……
VolatileRead()
读取最新版本的内存地址, VolatileWrite()
写入该地址,使该地址可供所有线程使用。 在变量上一致地使用VolatileRead()
和VolatileWrite()
与将其标记为volatile一样具有相同的效果。
看一下这个博客文章 ,通过示例解释差异……
为什么行int num = address; 在那儿 ? 他们已经有了明确保持价值的地址参数。
这是一个防御性的副本,以避免在我们进入方法时外部的某些东西改变值,将整数值复制到局部变量以避免从外部意外更改。
笔记
由于在Visual Basic中不存在volatile关键字,因此您只能选择使用一致的VolatileRead()
和VolatileWrite()
静态方法来实现c#中 volatile关键字的相同效果。
为什么行int num = address; 在那儿 ? 他们已经有了明确保持价值的地址参数。
address
不是int
。 它是一个int*
(所以它确实是一个地址)。 代码解除引用指针并将其复制到本地,以便在取消引用后发生屏障。
详细说明aleroot的答案。
Volatile.Read和Volatile.Write与Royi Namir的论证中的volatile修饰符相同。 但你可以明智地使用它们。
例如,如果使用volatile修饰符声明一个字段,则无论是读取还是写入操作,每次访问此字段都将从CPU寄存器中读取,这不是免费的,在大多数情况下这不是必需的,如果是不必要的性能,如果字段甚至有很多读操作。
想想你将私有单例变量声明为volatile并在属性getter中返回的场景,一旦初始化你不需要从CPU寄存器中读取它的根,因此你可以使用Volatile.Read / Write直到它的实例被创建,一旦创建,所有读取操作都可以作为普通字段完成,否则会受到很大的性能影响。
而您可以根据需要使用Volatile.Read或Volatile.Write。 最佳用途是声明不带volatile修饰符的字段,并在需要时使用Volatile.Read或Volatile.Write。