2012-06-12 9 views
6

私はtips and tricksポストで読んでいましたが、私はこれまでにやったことのないC#のものを試してみたいと思っていました。したがって、次のコードは実際の目的を果たすものではなく、何が起こるかを見るための「テスト関数」に過ぎません。あなたが見ることができるようにC#ThreadStatic + volatileメンバーが期待どおりに動作しない

private static volatile string staticVolatileTestString = ""; 
[ThreadStatic] 
private static int threadInt = 0; 

、私はThreadStaticAttribute揮発性キーワードテストしています:

とにかく、私は2つの静的なプライベートフィールドを持っています。

とにかく、私はこのようになりますテスト方法があります。

private static string TestThreadStatic() { 
    // Firstly I'm creating 10 threads (DEFAULT_TEST_SIZE is 10) and starting them all with an anonymous method 
    List<Thread> startedThreads = new List<Thread>(); 
    for (int i = 0; i < DEFAULT_TEST_SIZE; ++i) { 
     Thread t = new Thread(delegate(object o) { 
      // The anon method sets a newValue for threadInt and prints the new value to the volatile test string, then waits between 1 and 10 seconds, then prints the value for threadInt to the volatile test string again to confirm that no other thread has changed it 
      int newVal = randomNumberGenerator.Next(10, 100); 
      staticVolatileTestString += Environment.NewLine + "\tthread " + ((int) o) + " setting threadInt to " + newVal; 
      threadInt = newVal; 
      Thread.Sleep(randomNumberGenerator.Next(1000, 10000)); 
      staticVolatileTestString += Environment.NewLine + "\tthread " + ((int) o) + " finished: " + threadInt; 
     }); 
     t.Start(i); 
     startedThreads.Add(t); 
    } 

    foreach (Thread th in startedThreads) th.Join(); 

    return staticVolatileTestString; 
} 

私は、この関数から返された参照を期待したい何がこのように出力されます:

thread 0 setting threadInt to 88 
thread 1 setting threadInt to 97 
thread 2 setting threadInt to 11 
thread 3 setting threadInt to 84 
thread 4 setting threadInt to 67 
thread 5 setting threadInt to 46 
thread 6 setting threadInt to 94 
thread 7 setting threadInt to 60 
thread 8 setting threadInt to 11 
thread 9 setting threadInt to 81 
thread 5 finished: 46 
thread 2 finished: 11 
thread 4 finished: 67 
thread 3 finished: 84 
thread 9 finished: 81 
thread 6 finished: 94 
thread 7 finished: 60 
thread 1 finished: 97 
thread 8 finished: 11 
thread 0 finished: 88 

しかし、何を私はこれを取得しています:

thread 0 setting threadInt to 88 
thread 4 setting threadInt to 67 
thread 6 setting threadInt to 94 
thread 7 setting threadInt to 60 
thread 8 setting threadInt to 11 
thread 9 setting threadInt to 81 
thread 5 finished: 46 
thread 2 finished: 11 
thread 4 finished: 67 
thread 3 finished: 84 
thread 9 finished: 81 
thread 6 finished: 94 
thread 7 finished: 60 
thread 1 finished: 97 
thread 8 finished: 11 
thread 0 finished: 88 

出力の2番目の '半分'は期待どおりの出力ですThreadStaticフィールドは私が思ったように動作していますが)、最初の出力のいくつかが最初の 'half'から 'スキップされました'ようです。

さらに、最初の '半分'のスレッドは順不同ですが、Start()を呼び出すとすぐにスレッドがすぐに実行されないことを理解しています。代わりに内部OSコントロールがスレッドが適切に見えるように開始します。 EDIT:私の脳は、だから、連続した番号


を逃すので、彼らは、実際に、私はちょうど彼らが思っていたしていないん、私の質問はありません:私は中にいくつかの行を失わせるために間違って何が起こっています最初の '半分'の出力?たとえば、 'のスレッド3の設定threadIntは84'の行はどこですか?

+0

私はあなたのコードをいくつか実行して、いつも期待される出力を得ます... –

+0

私はその行のどこかに賭けたいと思います。文字列の+ =は以前のスレッドがそれを設定する前に値を取得しています。新しいスレッドは新しい値で上書きします(したがって、スレッド1は出力をスキップしました)。 – Charleh

+0

@DannyChen私はクアッドコアを利用していますが、それはアプリケーションで実行される唯一のコードではありません。それらのいずれかが意味をなさないかどうかは分かりません。もしあなたが望むなら、私はどこかでコード全体をアップロードすることができます(それは2つの.csファイルのみです)。 – Xenoprimate

答えて

7

スレッドは同時に実行されています。スレッド1がstaticVolatileTestString

  • スレッド2がstaticVolatileTestString
  • スレッド3がstaticVolatileTestString
  • スレッド1は、ものを追加し、
  • バックスレッド2をstaticVolatileTestString読み書き読み込み読み込み

    staticVolatileTestString += Environment.NewLine + "\tthread " + ((int) o) + " setting threadInt to " + newVal; 
    
    1. :この何概念的に起こるですスタンプを追加してstaticVolatileTestStringを返します
    2. スレッド3 stuffを追加してstaticVolatileTestStringを返します。

    これは、行が失われる原因となります。揮発性はここでは役に立たない。文字列を連結するプロセス全体がアトミックではありません。あなたはこれらの操作の周りのロックを使用する必要があります。

    private static object sync = new object(); 
    
    lock (sync) { 
        staticVolatileTestString += Environment.NewLine + "\tthread " + ((int) o) + " setting threadInt to " + newVal; 
    } 
    
  • +0

    うん、同じことのために爆発のためだった揮発性のものではないことに気づいた、それをクリアより良い説明:Dハハ+1 – Charleh

    2

    MSDNキーワードvolatilehereを何説明:

    volatileキーワードは、フィールドが 複数の同時実行スレッドによって変更される可能性があることを示しています。 volatileと宣言されたフィールドは、単一のスレッドによって にアクセスすることを前提とするコンパイラの最適化の対象にはなりません。これにより、フィールドに最新の値 が常に存在することが保証されます。これはあなたの例では、意味

    、何が起こることはについてあり、この(これは時々変えることができ、スケジューラによって異なります):

    • スレッド0がstaticVolatileTestStringの外に列を読んでいますバックstaticVolatileTestString
    012へ
  • スレッド0が文字列を書いている
  • スレッド0が追加された '88のスレッド0設定threadInt'

    これで今のところ、その後予想のように:

    • 1-4 staticVolatileTestString
    • スレッド1の外に列を読んでいるスレッドが追加された 'スレッド1設定threadInt 97に'
    • スレッド2
    • スレッド2 '11にスレッド2設定threadInt' スレッド1、2、3を読み、追加、および
    • を書いている...バック staticVolatileTestString
    • に文字列を書き込んで追加されます
    • スレッド4バックstaticVolatileTestString
    • にというように文字列を書いている...

    はここに何が起こったのかを参照してください?スレッド4は、文字列 'thread 0 setting threadInt 88'を読み込み、 'thread 4 ...'を追加して書き戻し、スレッド1,2,3が文字列に書き込まれたものすべてを上書きします。

  • +0

    スレッドは値を同時に読み込んでおり、別々の操作として書いていると言っていますか?そのような場合、私は(私がいた揮発性の考えたものであるが、私はそれが実際には何のキャッシング、権利がないことを確認したと思う?)フィールドのロックのいくつかの並べ替えを必要とするだろうEDITは:ルセロの答えは、この – Xenoprimate

    +0

    @Motigを確認します:ロックは、文字列を読み込んだり、書き戻したりするときにそこにありますが、間にはありません。注意:文字列は不変であるため、新しい文字列インスタンスが作成され、そのインスタンスへの参照がスレッドに書き戻されて変数に戻されます。 –

    +2

    @Motig:volatileキーワードは、変更可能なプリミティブデータ型(bool、int、doubleなど)で大きく機能します。しかし、文字列では、専用のロックを設定し、文字列を読み込んで変更して書き戻し、ロックを解除したいとします。 –

    関連する問題