易失性与易失性读/写?

时间:2013-02-23 09:50:40

标签: c# multithreading .net-4.0 volatile

我找不到 VolatileRead / write的任何示例(尝试...)但仍然:

何时我应该使用volatile vs VolatileRead

AFAIK volatile的全部目的是创建 half 围栏,以便:

  • 对于 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;在那里?他们已经有了明确保持价值的地址参数。

4 个答案:

答案 0 :(得分:36)

你永远不应该使用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放弃了all hope,并且不再劝阻所有人使用它。假设您可以比语言和框架提供的原语做得更好,通常非常不明智。 Volatile类的备注部分带来了重点:

  

在正常情况下,C#lock语句,Visual Basic SyncLock语句和Monitor类提供了同步访问数据的最简单且最不容易出错的方式,而Lazy类提供了一种编写延迟初始化代码的简单方法没有直接使用双重检查锁定。

答案 1 :(得分:7)

当您需要对代码应用围栏的方式进行更细粒度的控制时,您可以使用static Thread.VolatileReadThread.VolatileWrite

声明变量volatile意味着编译器不会缓存它的值并始终读取字段值,并且在执行写入时,编译器会立即写入指定的值。

两种方法Thread.VolatileReadThread.VolatileWrite使您能够在不将变量声明为volatile的情况下进行更精细的控制,因为您可以决定何时执行易失性读取操作以及何时执行易失性写入,没有限制读取没有缓存并立即写入当你声明变量volatile时,你会有更多的控制和更多的自由......

VolatileRead()读取最新版本的内存地址,VolatileWrite()写入该地址,使地址可供所有线程使用。在变量上一致地使用VolatileRead()VolatileWrite()与将其标记为易失性具有相同的效果。

看看this blog post,通过示例解释差异......

  

为什么行int num = address;在那儿 ?他们已经有了   解决了明确保持价值的论点。

这是一个防御性的副本,以避免在我们进入方法时外部的某些东西改变了值,整数值被复制到局部变量以避免从外部意外更改。

备注

由于在 Visual Basic 中,volatile关键字不存在,因此您只能选择使用一致的VolatileRead()VolatileWrite()静态方法来实现相同的效果 c#中的volatile关键字。

答案 2 :(得分:4)

  

为什么行int num = address;在那儿 ?他们已经有了   解决了明确保持价值的论点。

address不是int。它是int*(所以它确实是一个地址)。代码解除引用指针并将其复制到本地,以便在取消引用后发生屏障。

答案 3 :(得分:1)

详细说明aleroot的答案。

Volatile.Read和Volatile.Write与Royi Namir的论点中的volatile修饰符相同。但你可以明智地使用它们。

例如,如果使用volatile修饰符声明一个字段,那么无论是读取还是写入操作,每次访问该字段都将从CPU寄存器中读取,这不是免费的,在大多数情况下不需要这样做,并且不需要如果字段甚至有很多读操作,则性能会受到影响。

想想你将私有单例变量声明为volatile并在属性getter中返回的场景,一旦初始化你不需要从CPU寄存器读取它的根,因此你可以使用Volatile 。读取/写入直到它的实例创建,一旦创建,所有读取操作都可以作为普通字段完成,否则会对性能造成重大影响。

您可以根据需要使用Volatile.Read或Volatile.Write。 最佳用途是声明不带volatile修饰符的字段,并在需要时使用Volatile.Read或Volatile.Write。