38

C# 4 の概要 (強くお勧めします) では、次のコードを使用して MemoryBarrier の概念を示します (A と B が異なるスレッドで実行されたと仮定します)。

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);
    }
  }
}

彼らは、バリア 1 と 4 がこの例が 0 を書き込むのを防ぎ、バリア 2 と 3 が新鮮さを保証することを述べています。B が A の後に実行された場合、読み取り_completeがtrueと評価されることを保証します。

私は本当にそれを得ていません。バリア 1 と 4 が必要な理由を理解していると思います: _answer への書き込みを最適化して_completeへの書き込みの後に配置することは望ましくなく(バリア 1)、_answer がキャッシュされないようにする必要があります(バリア 4) 。 . また、バリア 3 が必要な理由も理解していると思います。A が_complete = trueを書き込んだ直後まで実行された場合、B は正しい値を読み取るために_completeを更新する必要があります。

なぜバリア 2 が必要なのかわかりません。私の一部は、おそらくスレッド 2 (B を実行中) がすでにif(_complete)まで実行されていたため (ただし、含まれていないため)、 _completeが更新さ れていることを確認する必要があると言っています。

ただし、これがどのように役立つかわかりません。A で_completeが true に設定されていても、B メソッドでは_completeのキャッシュ (false) バージョンが表示される可能性はありませんか? つまり、スレッド 2 が最初の MemoryBarrier の後までメソッド B を実行し、次にスレッド 1 がメソッド A を_complete = trueまで実行し、それ以上実行せず、スレッド 1 が再開してif(_complete)をテストした場合、そうでない場合はfalseになりますか?

4

2 に答える 2

30

バリア#2は、書き込み_completeがすぐにコミットされることを保証します。そうしないと、キューに入れられた状態のままになる可能性があります。つまり、揮発性の読み取りを効果的に使用したとしても、 _completeinの読み取りでBは変更が発生しません。AB

もちろん、この例では、A書き込み後に何も実行されないため、問題を完全に正当化することはできません_complete。つまり、スレッドが早期に終了するため、書き込みはとにかくすぐにコミットされます。

ifがまだ評価できるかどうかというあなたの質問に対する答えfalseは、まさにあなたが述べた理由のためにイエスです。しかし、この点に関して著者が言っていることに注意してください。

バリア1と4は、この例が「0」を書き込むのを防ぎます。バリア2と3は、鮮度を保証します。バリア2は、BがAの後に実行された場合に、_completeを読み取るとtrueと評価されることを保証します。

「BがAを追いかけた場合」の強調は私のものです。確かに、2つのスレッドがインターリーブする場合があります。しかし、作者はおそらくこのシナリオを無視して、どのようにThread.MemoryBarrier機能するかについての彼の主張をより単純にしました。

ちなみに、私のマシンでバリア#1と#2がプログラムの動作を変更する例を考案するのに苦労しました。これは、書き込みに関するメモリモデルが私の環境で強力だったためです。おそらく、マルチプロセッサマシンを使用している場合、Monoを使用している場合、またはその他の異なるセットアップを使用している場合は、それを実証できたはずです。もちろん、障壁#3と#4を取り除くことが影響を及ぼしたことを示すのは簡単でした。

于 2010-08-16T16:41:30.963 に答える
3

この例は、次の 2 つの理由で不明確です。

  1. フェンスで何が起こっているかを完全に示すには単純すぎます。
  2. Albahari には、x86 以外のアーキテクチャの要件が含まれています。MSDNを参照してください。

次のことを考えると、より明確になります。

  1. メモリ バリア (ここではフル バリア - .Net はハーフ バリアを提供しません) は、読み取り/書き込み命令がフェンスを飛び越えないようにします (さまざまな最適化のため)。これにより、フェンスの後のコードがフェンスの前のコードの後に​​実行されることが保証されます。
  2. 「このシリアル化操作は、MFENCE 命令に続くロードまたはストア命令がグローバルに表示される前に、プログラム順序で MFENCE 命令に先行するすべてのロードおよびストア命令がグローバルに表示されることを保証します。」ここを参照してください。
  3. x86 CPU には強力なメモリ モデルがあり、書き込みがすべてのスレッド/コアに対して一貫して表示されることが保証されます (したがって、バリア #2 と #3 は x86 では不要です)。ただし、読み取りと書き込みがコード化された順序のままであるとは限らないため、バリア #1 と #4 が必要です。
  4. メモリ バリアは非効率的であり、使用する必要はありません (同じ MSDN 記事を参照してください)。私は個人的に Interlocked と volatile を使用しています (正しく使用する方法を知っていることを確認してください!!)。これらは効率的に機能し、理解しやすいものです。

Ps。この記事では、x86 の内部の仕組みをうまく説明しています。

于 2017-02-27T09:14:35.150 に答える