挥发性的古怪

Tud*_*dor 9 c# multithreading

我想大多数人都知道在Release模式下构建时会发生以下问题(代码取自C#中的Threading):

static void Main()
{
  bool complete = false; 

  var t = new Thread (() =>
  {
    bool toggle = false;
    while (!complete) toggle = !toggle;
  });

  t.Start();
  Thread.Sleep (1000);

  complete = true;
  t.Join();        // Blocks indefinitely
}
Run Code Online (Sandbox Code Playgroud)

由于编译器优化缓存了值,complete从而阻止子线程看到更新的值.

但是,改变上面的代码:

class Wrapper
{
    public bool Complete { get; set; }
}

class Test
{
    Wrapper wrapper = new Wrapper();

    static void Main()
    {
        var test = new Test();
        var t = new Thread(() =>
        {
            bool toggle = false;
            while (!test.wrapper.Complete) toggle = !toggle;
        });

        t.Start();
        Thread.Sleep(1000);

        test.wrapper.Complete = true;
        t.Join();        // Blocks indefinitely
    }
}
Run Code Online (Sandbox Code Playgroud)

使问题消失(即子线程能够在1秒后退出)而不使用volatile内存栅栏或任何其他引入隐式栅栏的机制.

添加完成标志的封装如何影响其在线程之间的可见性?

Ale*_*kov 6

我想你的答案在你的问题中:

由于编译器优化缓存完整的值,从而阻止子线程看到更新的值.

只要有意义/被认为安全合理地实施,就会执行编译器/ JIT优化.因此,您发现未按预期方式执行优化的情况 - 可能有充分的理由(某人检测到此使用模式和阻止优化)或者恰好没有进行优化(最有可能).

  • 关键是这是未定义的行为.编译器团队明天可以决定以与第一个示例相同的方式优化属性,或者他们无法优化第一个(或者不同地优化),以便它不会停止. (5认同)