.Net:易失性32位与非易失性64位&线程安全。处理64位的最佳方法是什么?

时间:2012-01-19 13:02:52

标签: .net multithreading 32bit-64bit volatile

我理解volatile会阻止某些(但不是全部)优化对变量执行。虽然文档在这个主题上有点令人困惑(例如维基百科和MSDN的矛盾),但据我所知,volatile正在应用半内存屏障,这样可以防止重新排序操作。 (参考Albahari)。

我也明白它会阻止寄存器的使用,这意味着由于例如循环中的变量提升,读取永远不会过时。

我从经验中也知道,编译器对不同的数据类型进行了不同的(未记录的AFAIK)优化,使得该区域有些不可预测。

然而,对我来说仍然完全不清楚。像long这样的64位值不能用volatile来修饰。

所以我的问题是如何处理这些变量,以便它们被视为等效的易失性32位值类型?

这让我觉得不一致,因为我不相信应该区别对待32位和64位值。

修改

此外,为什么内存屏障保证编译器会编写ASM来从RAM而不是寄存器中获取值?我理解为什么volatile会这样做。

3 个答案:

答案 0 :(得分:2)

这只是一个32位抖动的限制,就像普通的x86一样。它不能保证变量比在4的倍数的地址上更好地对齐。主要是因为堆分配器不能保证更好的工作。并且没有努力使堆栈地址保持比4更好。因此,64位变量可能跨越高速缓存行的边界。因此不能是原子的。

答案 1 :(得分:1)

从Eric Lippert看看这个伟大的blog,并按照文章的结论;-)
特别是在32个系统上访问64位长的ais不是原子的!

答案 2 :(得分:0)

这最初是一个评论,但后来成了答案。

你不能用Thread.MemoryBarrier来实现类似的东西吗?虽然很烦人,但我认为你不能拥有一个不稳定的longs是合理的,因为编译器不能保证在所有平台上都支持;而对于32位值,它可以。

Thread.MemoryBarrier是一种在运行时告诉操作系统代表您执行此操作的方法;将问题卸载到操作系统;就像Interlocked方法不是执行相同操作的机器代码指令的简写一样 - 因为它们不能保证在每个CPU上都可用。

正如您正确指出的那样,波动性在许多层面上得到强制执行:至少使用编译器,运行时和CPU。在这种情况下,.Net可以保证所有三个32位值,因为32位无处不在。但是,通过.Net,不一定可以为64位提供CPU支持;并且只勾选3个方框中的2个不足以保证代码仍然有用。

修改(回复您在问题末尾的其他评论)

我对MemoryBarrier的理解是,它是一个操作系统强制执行的信号,要求所有线程刷新所有写入RAM的信号;然后强制当前线程从RAM重新加载(返回寄存器),从而确保当前线程具有最新版本的值。