3

重複の可能性:
Thread.MemoryBarrier() が必要な理由

O'Reilly の C# からの要約:

class Foo
{
    int _answer;
    bool _complete;
    void A()
    {
        _answer = 123;
        Thread.MemoryBarrier(); // Barrier 1
        _complete = true;
        Thread.MemoryBarrier(); // Barrier 2
    }
    void B()
    {
        Thread.MemoryBarrier(); // Barrier 3
        if (_complete)
        {
            Thread.MemoryBarrier(); // Barrier 4
            Console.WriteLine (_answer);
        }
    }
}

メソッド A と B が異なるスレッドで同時に実行されたとします。


著者は次のように述べています。「バリア 1 と 4 により、この例では「0」を書き込むことができなくなります。バリア 2 と 3 により、鮮度が保証されます。B が A の後に実行された場合、_complete の読み取りが true と評価されることが保証されます。」

私の質問は次のとおりです。

  1. バリア 4 が必要な理由 バリア1じゃ足りない?
  2. なぜ 2 と 3 が必要なのですか?
  3. 私が理解していることから、バリアは次の命令の後にその場所の前に命令を実行することを防ぎます、私は正しいですか?
4

2 に答える 2

6

メモリ バリアは、メモリからの読み取りとメモリへの書き込みに順序制約を適用します。つまり、バリアの前のメモリ アクセス操作は、バリアの後のメモリ アクセスの前に発生します。

  1. バリア 1 と 4 には補完的な役割があります。バリア 1 はへの書き込みが への書き込み_answerの前に発生_completeすることを保証し、バリア 4 は からの読み取りがから_completeの読み取りの前に発生することを保証し_answerます。バリア 4 がなく、バリア 1 があるとします。実行中の他のスレッドに123書き込まれる_answer前にtrue書き込まれることが保証されていますが、読み取り操作の順序が変更されている可能性があるため、読み取る前に読み取られる可能性があります。同様に、バリア 1 が削除され、バリア 4 が保持されている場合:_completeB()_answer_complete_completeB()_answer_complete_answer実行中の他のスレッドによってA()

  2. バリア 2 と 3 は鮮度を保証します。バリア 2 の後にバリア 3 が実行されると、バリア 2 が実行さA()れた時点で実行中のスレッドから見える状態は、バリア 3 が実行された時点で実行中のスレッドから見えるようになりますB()。完了後にこれら 2 つのバリアをB()実行するA()と、 による変更が反映されない可能性がありますA()。特に、バリア 2 は、書き込まれた値が_complete実行中のプロセッサによってキャッシュされるのを防ぎ、A()プロセッサに強制的にそれをメイン メモリに書き出させます。同様に、バリア 3 は、実行中のプロセッサB()が次の値をキャッシュに依存することを防ぎます。_completeメインメモリからの読み取りを強制します。ただし、メモリ バリア 2 および 3 が存在しない場合に新鮮さの保証を妨げる可能性があるのは、古いキャッシュだけではないことに注意してください。メモリ バス上の操作の並べ替えは、このようなメカニズムの別の例です。

  3. メモリバリアは、メモリアクセス操作の効果がバリアを越えて順序付けられることを保証するだけです。他の命令 (たとえば、レジ​​スタ内の値をインクリメントする) は、引き続き並べ替えることができます。

于 2011-11-13T21:41:40.813 に答える
1

では、メモリ バリアにより、最適化コンパイラが命令を並べ替えることができなくなります。これは、バリアの前の命令は、バリアの後の命令の後に実行できないことを意味します。バリアにはいくつかの種類がありますが、詳しくは説明しません。また、メモリの順序付けが弱い CPU では、命令の順序が変更され、デッドロックが発生する可能性があります。そう:

  1. メソッド B を実行するスレッドに _answer の最新の値を読み取らせる (つまり、0 ではなく 123 を読み取る) には、バリア 4 が必要です。リリース モードでコンパイルすると、コンパイラがコードを最適化し、B を実行しているスレッドが 0 を読み取ることができるように命令を並べ替えることがありますが、記述した命令が論理的にこれを不可能にする場合でも (_answer が_complete の前に割り当てられます)。
  2. バリア 2 と 3 は、B を実行しているスレッドが A の後に実行された場合に _complete を false として読み取る方法がないように、並べ替え (および _complete の値のキャッシュ) も防ぎます。
  3. 答えは上記です。
于 2011-11-13T21:28:33.943 に答える