指令重新订购

use*_*477 4 .net c# 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
}
Run Code Online (Sandbox Code Playgroud)

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

//memory operations
ClassInstance.Foo();
//memory operations
Run Code Online (Sandbox Code Playgroud)

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

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

如果是这样,当它被编译器内联时,它仍会有这种"内存屏障"行为吗?

Ste*_*ngs 6

C#语言规范可以帮助回答这个问题.关于执行令的部分有这样的说法:

3.10执行顺序

执行C#程序,以便在关键执行点保留每个执行线程的副作用.....执行环境可以自由更改C#程序的执行顺序,但受以下限制:

  • 数据依赖性保留在执行的线程中.也就是说,计算每个变量的值,就好像线程中的所有语句都以原始程序顺序执行一样.

  • 保留初始化排序规则(第10.5.4节和第10.5.5节).

  • 关于易失性读写(第10.5.3节),保留了副作用的顺序.

(还有更多我遗漏了;规格很可读,所以我建议看看你是否真的想深入了解细节).

本质上,规则可以被认为是"只要执行线程无法观察到差异,抖动就可以重新排列执行顺序".但是,其他线程可能会观察到差异.在Eric Lippert在Coverity博客上的一篇文章中,他说:

... CPU可以作为优化选择[重新排列执行顺序],前提是当前线程无法检测到这样做.但是另一个线程可以观察到这个事实......

因此,如果操作顺序对于其他线程以及当前线程很重要,那么您将需要创建一个"关键执行点"; 最简单的方法是用锁来包围语句.

  • 谢谢你的呐喊.我将关注那篇关于虚拟读写重新排序可能导致程序不变量大约两周的博客文章. (2认同)