2012-02-29 2 views
2

をマルチスレッド:CPUのキャッシュ口座にCPUのキャッシュとすべての可能なコンパイラの最適化を取る行動

  1. がコンソールに書き込まれることを「false」のため、それは可能ですか?
  2. オプション1は、オブジェクト参照とフィールドのフレッシュさを保証するメモリフェンスを設定していますか?
  3. オプション2はオプション1よりも安全性が低いですか?当然の

    class Program 
    { 
        class Test 
        { 
         internal bool value = false; 
        } 
    
        static void Main(string[] args) 
        { 
         Test test = new Test(); 
    
         //Option 1 
         ThreadPool.QueueUserWorkItem((s) => 
         { 
          Thread.Sleep(1000); 
          Console.WriteLine(((Test)s).value); 
         }, test); 
    
         //Option 2 
         ThreadPool.QueueUserWorkItem((s) => 
         { 
          Thread.Sleep(1000); 
          Console.WriteLine(test.value); 
         }); 
    
         test.value = true; 
         Console.ReadLine(); 
        } 
    } 
    

答えて

2
  1. はい、。メインスレッドがtrueに設定する前に、オプション1またはオプション2のワーカースレッドがfalse値を出力しないようにする同期はありません。考案された例では、Thread.Sleepを使用して現実的に発生しないようにしていますが、ここではの同期はありません。

  2. いいえ、オプション1は、単に代表に転送するためのスレッドプールへsへの参照を送信しています。起こるメモリーフェンシングは、実装の詳細とみなされるべきであり、私が知っている限り、何も起こりません。上記のThread.Sleep()以外のものは、メインスレッドがtrueに設定する前に、オプション#1のワーカースレッドが "false"を表示しないようにするものはありません。

  3. オプション2を使用すると、あまり不自然な例で変更することができる変数の上に閉じていることにはあまり安全です。 Eric Lippertのblog post on the subject of closing over loop variablesを参照してください - 同じアドバイスがオプション#2に当てはまります。

2

testが再割り当てされていない)提示された特定のシナリオの点で1と2との間の有意な差は存在しません。本当の疑問は、「行動は保証されていますか?」それに正しく答えるために、私はlockを見ています。 volatileを使用することはできますが、それはきわめてまれにしか正しく理解されません。間違った理由でよく働くことがあります。

疑問がある場合は、lockのようなものを使用してください。

1

作業項目を実行する前に、ThreadPoolの初期化に時間がかかるため、他のスレッドで作業を開始する前に、すでに値がtrueに変更されています。値をtrueに変更する前にいくつかのロックとわずかな遅延を追加すると、どちらの場合もfalseになります。

class Program 
{ 
    class Test 
    { 
     internal object lockValue = new Object(); 
     internal bool value = false; 
    } 

    static void Main(string[] args) 
    { 
     Test test = new Test(); 

     //Option 1 
     ThreadPool.QueueUserWorkItem((s) => 
     { 
      lock (test.lockValue) 
      { 
       Thread.Sleep(1000); 
       Console.WriteLine(((Test)s).value); 
      } 
     }, test); 

     //Option 2 
     ThreadPool.QueueUserWorkItem((s) => 
     { 
      lock (test.lockValue) 
      { 
       Thread.Sleep(1000); 
       Console.WriteLine(test.value); 
      } 
     }); 

     Thread.Sleep(100); 
     lock (test.lockValue) 
     { 
      test.value = true; 
     } 
     Console.ReadLine(); 
    } 
} 
+0

をコード。あなたのマシン**上でスレッド**を開始することは100ミリ秒以下になるので、オプション#1とオプション#2のロックステートメントはメインスレッドのロックステートメントの前に置かれます。 –

0
  1. テスト値型だった場合、それは何かを重要だったかもしれませんが、コードが書かれているようにはしていません。

  2. 参照型を通過し、クロージャを使用して、ほぼ同一であり、(C#で)クロージャだけの関数に渡されるほとんどのコンテナクラスを作成します。デリゲートも少し違って処理されますが、リフレクターを使用して何が起こるかを正確に見ることができます。あなたが書いたものを

  3. が完全に未定義の動作では、最初に起動されますどのスレッド(賢明なC#)を知っている、または設定行かどうか最初に起こることはできません。

  4. スレッド間でメモリを共有しないようにします。あなたはデータをコピーすることができます、あなたは不変を使用することができますが、あなたは変更可能なデータを共有しないでください。パフォーマンス上の理由からまず第一に、コピーは通常は高速で同期します。

  5. メモリを共有していても、実際に何をしているのかわからない限り、メモリバリアや他の種類の暗黙的な同期を避けて、異なるプラットフォーム(32,64、IA64) ...つまらない...

と、この読み:これは、スレッドを起動するにはいくつかの時間があることを示しているが、あなたが得る結果は、元のポスターのと同じように偶然です http://www.albahari.com/threading/

関連する問題