我有一个关于在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()中重新排序内存操作(从前面的示例//内存操作)到外部内存操作是否合法。
此外,函数调用(不内联)在某种意义上是“生成内存障碍”。与其中一样,在函数调用之前或之后发生的内存操作不能与函数调用中的内存操作重新排序。
如果是这样的话,当它被编译器内联时,它还会有这种“内存屏障”行为吗?
发布于 2014-03-15 06:39:14
C#语言规范可以帮助回答这个问题。关于执行命令的一节规定如下:
3.10执行命令 C#程序的执行继续进行,以便在关键执行点保留每个执行线程的副作用。....The执行环境可以自由地更改C#程序的执行顺序,但须受以下约束:
(我遗漏了更多的内容;规范非常容易读,所以我建议如果您真的想了解具体的细节,请看一看)。
从本质上说,规则可以被认为是“只要执行线程无法观察到差异,抖动就可以重新安排执行顺序”。但是,其他线程可能会观察到这些差异。在Eric Lippert在Coverity博客上发表的文章中,他说:
如果当前线程无法检测到执行顺序,则...the CPU可以作为优化选择重新安排执行顺序。但另一条线索可以观察到这个事实..。
因此,如果操作顺序对于其他线程和当前线程都很重要,那么您将需要创建一个“关键执行点”;最简单的方法可能是用锁包围语句。
发布于 2014-05-29 19:44:22
在讨论指令重新排序时,请记住,通常有两个(或更多)线程在运行。规范中的执行顺序子句基本上是直觉概念的形式定义,即线程应该按照程序员指定的顺序感知副作用。没有它,应用程序就会有不确定的行为。
这个话题的真正症结在于其他线程如何看待副作用。这就是关于易失性读写的第三点发挥作用的地方。正因为如此,所有的写(在我所知道的.NET框架的所有版本中)都具有发布围栏的语义。
我喜欢使用箭头符号来帮助可视化对指令重新排序优化的约束。我使用↑箭头表示释放围栏,使用↓箭头表示获取围栏。任何东西都不允许在↑箭头或↓箭头上方浮动。把箭头想象成把所有东西都推开。使用这个箭头符号并假设写仍然具有release语义,那么您的代码将如下所示。
void Foo()
{
↑
_a = 1; // Write 1
↑
_b = 1; // Write 2
↑
_c = 1; // Write 3
}希望现在可以更容易地看到,由于箭头阻止了它的运动,所以不允许任何写操作从另一个写出来。这意味着,实际上,其他线程将以与执行Foo的线程相同的顺序来感知这些写入。
https://stackoverflow.com/questions/22420368
复制相似问题