4

以下のケースでは、ワーカー スレッド間で書き込みの競合がない場合でも、ロックまたは揮発性が必要ですか? 「G」で「Peek」アクセスが必要ない場合の答えの違い。

class A 
{
   Object _o; // need volatile (position A)?
   Int _i;    // need volatile (position B)?

   Method()
   {
      Object o;
      Int i;

      Task [] task = new Task[2]
      {
         Task.Factory.StartNew(() => { 
              _o = f1();   // use lock() (position C)?
              o  = f2();   // use lock() (position D)?
         } 
         Task.Factory.StartNew(() => { 
              _i = g1();   // use lock() (position E)?
              i  = g2();   // use lock() (position F)?
         }          
      }

      // "Peek" at _o, _i, o, i (position G)?

      Task.WaitAll(tasks);

      // Use _o, _i, o, i (position H)?
}
4

3 に答える 3

7

安全な方法は、最初からこれを行わないことです。そもそもあるスレッドに値を書き込んで、別のスレッドで値を読み取らないでください。スレッド間で変数を変更するタスクを作成するのではなく、値を必要とするスレッドに値を返すaTask<object>および aを作成します。Task<int>

スレッド間で変数への書き込みに熱中している場合は、2 つのことを保証する必要があります。1 つ目は、ジッタが最適化を選択しないため、読み取りと書き込みが時間内に移動してしまうことです。2 つ目は、メモリ バリアが導入されることです。メモリバリアは、プロセッサが特定の方法で時間内に読み取りと書き込みを移動することを制限します。

ブライアン・ギデオンが彼の回答で指摘しているように、WaitAll.

私が言ったように、私はそもそもこれをしません。強制された場合、少なくとも書き込み先の変数を揮発性としてマークします。

于 2013-07-22T19:14:47.177 に答える
5

Object参照型 (つまり) およびワード サイズの値型 (つまりint32 ビット システム)への書き込みはアトミックです。これは、値 (位置 6) をのぞくと、古い値または新しい値のいずれかを取得できることを確認できますが、それ以外のものは取得できないことを意味します (大きな構造体などの型がある場合は、それを接合することができ、書き込みの途中で値を読み取ることができました)。古い値を読み取る潜在的なリスクを受け入れる意思がある限り 、またはは必要ありません。lockvolatile

この時点でメモリバリアが導入されていないため (lockまたはvolatile両方を使用して add one)、変数が他のスレッドで更新されている可能性がありますが、現在のスレッドはその変更を監視していません。他のスレッドで変更された後、(潜在的に) かなりの時間、「古い」値を読み取っている可能性があります。を使用volatileすると、現在のスレッドが変数への変更をより早く確認できるようになります。

またはWaitAllがなくても、 の呼び出し後に適切な値が得られることを確認できます。lockvolatile

また、参照型への参照がアトミックに記述されていることは確認できますが、参照が参照する実際のオブジェクトへの変更の監視順序についてプログラムが保証するわけではないことに注意してください。バックグラウンド スレッドの観点から、インスタンス フィールドに割り当てられる前にオブジェクトが初期化されたとしても、その順序で発生しない場合があります。したがって、他のスレッドは、オブジェクトへの参照の書き込みを監視できますが、その参照に従って、初期化または部分的に初期化された状態のオブジェクトを見つけます。メモリ バリアを導入する (つまり、volatile変数を使用することで、ランタイムがそのような並べ替えを行うのを防ぐことができる可能性があるため、それが起こらないようにすることができます。これが、そもそもこれを行わない方がよい理由です。そして、クローズド オーバー変数を操作するのではなく、2 つのタスクが生成した結果を返すようにします。

WaitAll2 つのタスクが実際に終了したことを確認するだけでなく、メモリ バリアが導入されます。つまり、変数が最新であり、古い古い値を保持していないことがわかります。

于 2013-07-22T18:50:18.157 に答える