10

ほとんどの人は、リリース モードでビルドするときに発生する次の問題を認識していると思います (コードは 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
}

の値をキャッシュするコンパイラの最適化により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
    }
}

volatile、メモリフェンス、または暗黙のフェンスを導入するその他のメカニズムを使用せずに、問題を解消します (つまり、子スレッドは 1 秒後に終了できます) 。

完了フラグの追加されたカプセル化は、スレッド間の可視性にどのように影響しますか?

4

2 に答える 2

6

あなたの質問に答えがあると思います:

コンパイラの最適化により、complete の値がキャッシュされ、子スレッドが更新された値を認識できなくなります。

コンパイラー/JIT の最適化は、実装することが理にかなっている/安全で合理的​​であると見なされるたびに実行されます。したがって、最適化が期待どおりに実行されないケースが見つかりました-おそらく正当な理由がある (誰かがこの使用パターンを検出して最適化をブロックした) か、たまたま最適化されていない (ほとんどの場合)。

于 2013-01-21T18:41:57.510 に答える
0

最初のケースは、ローカル変数に対する単純なエイリアスの伝播です。コンパイラはこれを非常に積極的に行います ( http://en.m.wikipedia.org/wiki/Aliasing_(computing) ) 2 番目のケースでは、プロパティとメンバー変数の構文が似ているために同じように見えますが、メソッド呼び出し (getter /setter) であり、単純化することはできません。

于 2013-01-21T18:56:41.510 に答える