12

マルチスレッド アプリケーション用のロック フリー コードを実装するために、volatile変数 を使用しました。したがって、スレッドが変数値を更新し、その更新が行われた直後にスレッドがその変数を読み取ると、スレッド A から最近書き込まれた最新の値が表示されますvolatileAB

volatile を適用しても、書き込みとそれに続く読み取りがスワップされるのを防ぐことはできません。

この問題は、変数をThread.MemoryBarrier()取得する前に次のように配置することで解決できますか?volatile

private volatile bool _foo = false;

private void A()
{
    //…
    Thread.MemoryBarrier();
    if (_foo)
    {
        //do somthing
    }
}

private void B()
{
    //…
    _foo = true;
    //…
}

そして、これで問題が解決した場合。条件の 1 つでその値に依存する while ループがあるとします。Thread.MemoryBarrier()whileループの前に置くことは問題を解決する正しい方法ですか? 例:

private void A()
{
    Thread.MemoryBarrier();
    while (_someOtherConditions && _foo)
    {
        // do somthing.
    }
}

より正確に言うと_foo、スレッドがいつでも変数を要求したときに、変数が最も新しい値を与えるようにします。したがってThread.MemoryBarrier()、変数を呼び出す前に挿入すると問題が解決する場合は、Foo代わりにプロパティを使用して、そのプロパティの取得内で_foo実行できます。Thread.MemoryBarrier()

Foo
{
    get 
    {
        Thread.MemoryBarrier();
        return _foo;
    }
    set
    {
        _foo = value;
    }
}
4

5 に答える 5

11

「C# の概要」は正しいですが、そのステートメントは議論の余地があります。なんで?

  • 「揮発性」を伴わない「書き込み」に続く「読み取り」は、単一のスレッド内のロジックに影響を与える場合、とにかくプログラム順に発生することが保証されます
  • マルチスレッドプログラムでの「読み取り」の前の「書き込み」は、あなたの例ではまったく無意味です。

明確にしましょう。元のコードを取得します。

private void A() 
{ 
    //… 
    if (_foo) 
    { 
        //do something 
    } 
}

スレッド スケジューラが既に変数をチェックしているのに、コメント_fooの直前で中断された場合はどうなりますか? //do somethingその時点で、他のスレッドが の値を変更する可能_foo性があります。の値が false の場合に絶対do_somethingに回避する必要がある場合_fooは、ロックを使用するしかありません。

ただし、 がdo something突然_foofalse になったときに が実行されても問題ない場合は、volatile キーワードがニーズを十分に満たしていたことを意味します。

明確にするために、メモリバリアを使用するように指示しているすべてのレスポンダーは間違っているか、やり過ぎを提供しています。

于 2010-11-24T19:19:01.923 に答える
5

本は正しいです。
CLR のメモリ モデルは、ロード操作とストア操作の順序が変更される可能性があることを示しています。これは、揮発性変数非揮発性変数に当てはまります。

変数をvolatileonly として宣言することは、ロード操作が取得セマンティクスを持ち、ストア操作がリリースセマンティクスを持つことを意味します。また、コンパイラは、変数がシリアル化されたシングル スレッド方式でアクセスされるという事実を中継する特定の最適化の実行を回避します (たとえば、ループからのロード/ストアの巻き上げ)。

キーワードを単独で使用してvolatileもクリティカル セクションは作成されず、スレッドが魔法のように相互に同期することもありません。

ロック フリー コードを記述するときは、細心の注意を払う必要があります。単純なことは何もなく、専門家でさえ正しく理解するのに苦労しています。
解決しようとしている元の問題が何であれ、それを行うためのはるかに合理的な方法がある可能性があります。

于 2010-11-24T18:52:41.003 に答える
1

2番目の例ではThread.MemoryBarrier();、ループの状態をチェックするたびに最新の値を確実に取得できるように、ループの内側にも配置する必要があります。

于 2010-11-24T17:24:48.203 に答える
1

ここから引っ張ってきた...

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 と評価されることを保証します。

ループの例に戻ると、次のようになります...

private void A()
{
    Thread.MemoryBarrier();
    while (_someOtherConditions && _foo)
    {
        //do somthing
        Thread.MemoryBarrier();
    }
}
于 2010-11-24T17:32:16.277 に答える
0

メモリバリアに関する Microsoft 自身の言葉:

MemoryBarrier は、メモリの順序付けが弱いマルチプロセッサ システム (たとえば、複数の Intel Itanium プロセッサを使用するシステム) でのみ必要です。

ほとんどの場合、C# の lock ステートメント、Visual Basic の SyncLock ステートメント、または Monitor クラスを使用すると、データを簡単に同期できます。

于 2010-11-24T18:18:11.943 に答える