这个问题是关于记忆障碍的另一个问题的答案的扩展:
https://stackoverflow.com/a/3556877/13085654
假设您使用该代码示例并对其进行调整以使方法异步:
class Program
{
static bool stop = false;
public static void Main(string[] args)
{
var t = new Thread(async () =>
{
Console.WriteLine("thread begin");
while (!stop)
{
if (false)
{
await default(Task);
}
}
Console.WriteLine("thread end");
});
t.Start();
Thread.Sleep(1000);
stop = true;
Console.WriteLine("stop = true");
Console.WriteLine("waiting...");
t.Join();
}
}
显然,await
永远不会被命中,但它在方法中的存在以某种方式允许Release编译的程序完成:注释掉await default(Task);
会导致程序再次挂起(即使您将方法标记为async
(。这让我认为,编译器生成的状态机根据该方法是否包含至少一个await
而有很大不同。但是,比较实际命中的方法部分的IL显示出几乎相同的指令,两者都包含以下循环结构:
// start of loop, entry point: IL_0039
// [14 13 - 14 26]
IL_0039: ldsfld bool Program::stop
IL_003e: brfalse.s IL_0039
// end of loop
在IL级别,当存在await
时,我看不出C#编译器在做什么来注入内存屏障。有人知道这是怎么发生的吗?
有人知道这是怎么发生的吗?
您编写了一个依赖于未定义行为的程序。因此,结果是不确定的。任何一种实现都可以产生任何一种结果。如果您想依赖其中一个结果,那么这两个实现都是不可接受的。该语言不能保证,如果没有内存障碍,变量就不能观察来自另一个线程的更新,而只能保证它可以也可以不。如果您想强制读取更新的值,请使用内存屏障,因为这就是它的作用。要强制值为而不是,请使用该线程特定的变量副本。