像我这样的问题一直是asked,但我的有点不同。问题是,“为什么C#
类型System.Double
和System.Int64
等不允许使用volatile关键字?”
乍一看,我回答了我的同事,“好吧,在32位机器上,这些类型至少需要两个滴答才能进入处理器,.Net框架的目的是抽象出特定于处理器的细节。像那样。”他回答说:“如果由于处理器特定的问题导致你无法使用某个功能,那么它就不会抽象出任何东西!”
他暗示特定于处理器的细节不应该出现在使用框架的人身上,该框架“抽象”远离程序员的细节。因此,框架(或C#)应该抽象出那些并做它需要做的事情来为System.Double
等提供相同的保证(无论是信号量,内存障碍还是其他)。我认为框架不应该在volatile
上添加信号量的开销,因为程序员不期望使用这样的关键字这样的开销,因为32位类型不需要信号量。 64位类型的更大开销可能会让人感到意外,因此,更好的.Net框架只是不允许它,并且如果开销是可接受的,那么就可以在更大的类型上使用自己的信号量。
这导致我们调查volatile关键字的全部内容。 (见this页面)。该页面在注释中说明:
在C#中,在字段上使用volatile修饰符可确保对该字段的所有访问都使用VolatileRead或VolatileWrite。
嗯..... VolatileRead
和VolatileWrite
都支持我们的64位类型!!那么,我的问题是,
“为什么类型
C#
和System.Double
等System.Int64
中不允许使用volatile关键字?”
答案 0 :(得分:17)
他暗示特定于处理器的细节不应该出现在使用框架的人身上,该框架“抽象”远离程序员的细节。
如果您使用低锁技术(如易失性字段,显式内存屏障等),那么您在处理器特定细节的世界中完全。为了编写使用低锁技术的正确,可移植,强大的程序,您需要在深层精确地理解处理器是什么,并且不允许执行重新排序,一致性等等。
此功能的目的是说“我正在放弃单线程编程所保证的方便抽象,并通过深入了解我的处理器实现知识来获得性能提升。”当你开始使用低锁技术时,你应该期待更少的抽象,而不是更多的抽象。
你大概是因为某种原因而“走向金属”;你支付的价格是必须处理所述金属的怪癖。
答案 1 :(得分:12)
是。原因是您甚至无法在一次操作中阅读double
或long
。我同意这是一个糟糕的抽象。我有一种感觉,理由是以原子方式阅读它们需要付出努力,而且对于编译器来说它会非常聪明。因此,他们会让您选择最佳解决方案:lock
ing,Interlocked
等
有趣的是,它们实际上可以使用MMX寄存器在32位上原子读取。这就是Java JIT编译器的功能。它们可以在64位机器上原子读取。所以我认为这是设计上的严重缺陷。
答案 2 :(得分:5)
不是你问题的答案,但......
我非常确定您引用的MSDN文档在声明“使用字段上的volatile修饰符时保证对该字段的所有访问都使用VolatileRead或VolatileWrite”时是错误的。
直接读取或写入volatile
字段只会产生半栅栏(读取时为获取栅栏,写入时为释放栅栏)。
VolatileRead
和VolatileWrite
方法在内部使用MemoryBarrier
,这会产生全屏障。
Joe Duffy对并发编程知之甚少; this is what he has to say about volatile:
(顺便说一下,很多人都在想 负载和负载之间的差异 变量存储标记为volatile 并调用Thread.VolatileRead和 Thread.VolatileWrite。区别 是以前的API是 实施强于jitted 代码:他们实现获取/发布 通过发射完整围栏的语义 右边。 API更多 打电话也很贵,但至少 让你决定一个 以呼叫站点为基础的 个人装货和商店需要 MM保证。)
答案 3 :(得分:3)
这是遗产的简单解释。如果您阅读本文 - http://msdn.microsoft.com/en-au/magazine/cc163715.aspx,您会发现.NET Framework 1.x运行时的唯一实现是在x86计算机上,因此Microsoft有必要针对x86内存模型实现它。稍后添加了x64和IA64。所以基本内存模型总是x86之一。
它可以用于x86吗?我实际上不确定它是否可以完全实现 - 从本机代码返回的double的ref可以对齐到4个字节而不是8个。在这种情况下,所有原子读/写的保证都不再成立。