为什么方法调用将非易失性变量的值刷新到主线程?

jnm*_*nm2 5 c# multithreading

为什么吸气剂Val会模拟现场的波动val

我假设利用方法调用不是保持变量volatile的可靠方法.

(要试用它,构建版本并直接执行而不需要调试器.)

class Program
{
    private int val = 0;
    public int Val { get { return val; } }

    public static void Main()
    {
        var example = new Program();

        Task.Run(() => example.val++);

        while (example.val == 0) ; // Hangs if val is not volatile
        while (example.Val == 0) ; // Never seems to hang
    }
}
Run Code Online (Sandbox Code Playgroud)

Cam*_*ron 5

好吧,事实证明,允许抖动假设所有非易失性变量只能由一个线程访问(非常类似于C++ 11内存模型,其中并发访问非std::atomic<>变量调用未定义的行为).在这种情况下,抖动正在优化第一个循环loop: test eax, eax; je loop(它将变量访问提升到永远不会更新的寄存器),所以很明显它永远不会终止.

第二个循环生成组件,该组件读取相对于对象指针的值,因此最终它会看到新值(尽管可能与另一个线程上的其他写入无关,因为该变量不是易失性的).由于碰巧生成的装配,这是巧合.

为第一个(无限)循环生成的x86程序集:

003B23BA  test        eax,eax  
003B23BC  je          003B23BA  
Run Code Online (Sandbox Code Playgroud)

第二个(有限)循环的x86程序集:

002F2607  cmp         dword ptr [eax+4],0  
002F260B  je          002F2607
Run Code Online (Sandbox Code Playgroud)

由于允许抖动假设非易失性变量永远不会被其他线程触及,因此您只能依赖于volatile按预期工作(即使它出现在给定的情况下,如此,因为未来的优化(或不同的CPU架构)等等可能会以难以调试的方式破坏您的代码).