Volatile vs VolatileRead/Write?
本文关键字:Write VolatileRead vs Volatile | 更新日期: 2023-09-27 18:00:36
我找不到VolatileRead/write的任何示例(try…),但仍然:
我应该在什么时候使用volatile
和VolatileRead
?
AFAIK volatile
的全部目的是创建半个围栏,因此:
- 对于READ操作,当前操作之后的读/写(在其他线程上)不会在围栏之前通过。因此,我们读取了最新值
问题#1
那么为什么我需要volatileRead
呢?似乎CCD_ 5已经做了这项工作。
另外,在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;
线路?他们已经有了address参数,该参数显然持有该值。
您永远不应该使用Thread.VolatileRead/Write()。这是.NET 1.1中的一个设计错误,它使用了全内存屏障。这在.NET 2.0中得到了纠正,但他们无法再修复这些方法,必须添加一种由System.Threading.Volatile类提供的新方法。这是抖动知道的一个类,它将jit时的方法替换为适合特定处理器类型的版本。
可通过参考源获得的易失性类源代码中的注释告诉了故事(经过编辑以适合):
// 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已经放弃了所有的希望,完全不鼓励使用它。通常认为你可以做得比语言和框架提供的原语更好是非常不明智的。挥发性类别的备注部分将要点带回家:
在正常情况下,C#锁语句、Visual Basic SyncLock语句和Monitor类提供了同步数据访问的最简单、最不容易出错的方法,而Lazy类提供了一种简单的方法来编写延迟初始化代码,而无需直接使用双重检查锁。
当您需要对围栏应用于代码的方式进行更细粒度的控制时,可以使用static
Thread.VolatileRead或Thread.Volative.
声明变量volatile意味着编译器不缓存其值,而是始终读取字段值,并且在执行写入时,编译器会立即写入指定的值。
Thread.VolatileRead和Thread.VolatileWrite这两个方法使您能够在不将变量声明为volatile的情况下进行更细粒度的控制,因为您可以决定何时执行volatile读取操作和何时执行volatile写入,而无需像声明variale volatile时那样绑定为不缓存读取和立即写入,所以用糟糕的话来说,你有更多的控制和自由。。。
VolatileRead()
读取内存地址的最新版本,VolatileWrite()
写入该地址,使该地址对所有线程都可用。在变量上同时使用VolatileRead()
和VolatileWrite()
与将其标记为易失性具有相同的效果
看看这篇博客文章,它通过例子解释了区别。。。
为什么行int num=address;有吗?他们已经有了address参数,它显然持有该值。
这是一种防御复制,以避免外部的东西更改值,而我们在方法内部时,整数值被复制到局部变量,以避免来自外部的意外更改。
备注
由于在Visual Basic中不存在volatile关键字,因此您只能选择一致使用VolatileRead()
和VolatileWrite()
静态方法来实现与c#的volatile关键词相同的效果
为什么行int num=address;有吗?他们已经有了address参数,它显然持有该值。
CCD_ 17不是CCD_。它是一个int*
(所以它实际上是一个地址)。代码正在取消引用指针并将其复制到本地,以便在取消引用之后出现障碍。
详细阐述aleroot的答案。
Volatile.Read和Volatile.Write和RoyiNamir的论点中的Volatile修饰符相同。但你可以明智地使用它们。
例如,如果您用volatile修饰符声明一个字段,那么对该字段的每次访问,无论是读操作还是写操作,都将从非空闲的CPU寄存器中读取,这在大多数情况下是不需要的,如果字段甚至有很多读操作,则会对性能造成不必要的影响。
想象一下这样的场景:私有的singleton变量被声明为volatile,并在属性getter中返回,一旦初始化,您就不需要从CPU寄存器中读取它的根,因此您可以使用volatile。在创建实例之前进行读/写,一旦创建,所有的读操作都可以作为普通字段进行,否则会对性能造成很大影响。
而您可以根据需要使用Volatile.Read或Volatile.Write。最佳用途是声明不带volatile修饰符的字段,并在需要时使用volatile.Read或volatile.Write。