指令重新订购

时间:2014-03-15 06:23:22

标签: c# .net memory-barriers

我对C#/ .NET中的法律指令重新排序有疑问。

让我们从这个例子开始吧。我们在某个类中定义了此方法,其中_a,_b和_c是字段。

int _a;
int _b;
int _c;
void Foo()
{
   _a = 1; // Write 1
   _b = 1; // Write 2
   _c = 1; // Write 3
}

我们的呼叫环境看起来像这样。

//memory operations
ClassInstance.Foo();
//memory operations

我想知道当这个方法调用内联到函数调用时,什么样的法律指令重新排序是可能的。更具体地说,我想知道在Foo()中重新排序内存操作是否/何时合法,内存操作在它之外(来自我们之前的示例,//内存操作)。

此外,在某种意义上,函数调用(无内联)是否会产生内存障碍"。同样,在函数调用之前或之后发生的内存操作不能与函数调用中的内存操作重新排序。

如果是这样的话,它是否还会有这种记忆障碍"它被编译器内联时的行为?

2 个答案:

答案 0 :(得分:6)

答案 1 :(得分:3)

在讨论指令重新排序时请记住,通常有两个(或更多)线程在起作用。规范中的Execution Order子句基本上是直观思想的形式定义,即线程应该按照程序员指定的顺序感知副作用。没有它,应用程序将具有非确定性行为。

该主题的真正症结在于其他线程如何感知副作用。这是关于易失性读写的第3点发挥作用的地方。事实上,所有写入(在我所知道的所有.NET Framework版本中)都具有发布范围语义。

我喜欢使用箭头符号来帮助可视化对指令重新排序优化的约束。我使用↑箭头指示释放栅栏和↓箭头以指示获取栅栏。没有任何东西可以通过↑箭头向上漂移或越过↓箭头。想想箭头将一切推开。使用此箭头表示法并假设写入仍然具有发布范围语义,那么您的代码将如下所示。

void Foo()
{
   ↑
   _a = 1; // Write 1
   ↑
   _b = 1; // Write 2
   ↑
   _c = 1; // Write 3
}

希望现在更容易看到任何写入都不允许向下浮动到另一个写入,因为箭头会阻止其移动。这意味着其他线程实际上会以与执行Foo的线程中发生的顺序相同的顺序感知这些写入。

我描述了在问题herehereherehere,尤其是here中重新排序说明的其他方式。