最近在学习锁定机制和并发性时,我遇到了编译器指令重新排序。从那时起,即使没有对字段的并发访问,我对我编写的代码的正确性更加怀疑。我刚遇到一段这样的代码:

var now = DateTime.Now;
var newValue = CalculateCachedValue();
cachedValue = newValue;
lastUpdate = now;
lastUpdate = now 是否有可能在 cachedValue 分配新值之前执行?这意味着如果运行此代码的线程被取消,我将记录未保存的更新。据我现在所知,我必须假设是这种情况,我需要一个内存屏障。

但是否有可能在第二个语句之后执行第一个语句?这意味着 now 是计算之后而不是之前的时间。我想情况并非如此,因为涉及方法调用。但是,没有其他明确的依赖项可以阻止重新排序。方法调用/属性访问是隐式障碍吗?我应该注意指令重新排序的其他隐式约束吗?

最佳答案

.NET 抖动可以重新排序指令,是的。不变代码运动和公共(public)子表达式消除是重要的优化,可以使代码更快。

但这不会随意发生。优化器只有在知道重新排序不会产生任何不良副作用的情况下才会考虑这样的优化。为了让它知道,它首先必须内联一个方法或属性 getter 调用。而 DateTime.Now 永远不会发生这种情况,它需要一个操作系统调用,而且这些调用永远不会被内联。所以你很难保证在 var now = DateTime.Now; 之前或之后没有任何语句移动

它实际上必须有意义地移动代码并产生可衡量的 yield 。重新排序赋值语句没有意义,它不会使代码更快。不变代码移动是一种优化,适用于循环内的语句,将这样的语句移动到循环之前使其不会被重复执行是有返回的。在这个片段中完全没有这种风险。这里也看不到子表达式消除。

害怕优化器引起的错误有点像害怕走出去,因为你可能会被一道闪电击中。那个会发生。几率非常非常低。 .NET 抖动的一个很好的保证是它每天经过数百万次测试。

关于c# - 编译器指令重新排序的隐式限制?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/35405984/

10-13 08:28