6

ヒントとコツの投稿を読んでいて、これまでやったことのない C# のことを試してみようと思いました。したがって、次のコードは実際の目的には役立たず、何が起こるかを確認するための単なる「テスト関数」です。

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

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

ご覧のとおり、ThreadStaticAttributevolatileキーワードをテストしています。

とにかく、次のようなテストメソッドがあります。

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

出力の後半の「半分」は期待どおりです (これは、ThreadStatic フィールドが私が思ったように機能していることを意味すると思います) が、最初の出力のいくつかは前半の「半分」から「スキップ」されているようです。

さらに、前半の「半分」のスレッドは順不同ですが、Start(); を呼び出してもすぐにスレッドが実行されないことは理解しています。代わりに、OS の内部制御が適切と思われるスレッドを開始します。 編集: いいえ、そうではありません。実際には、脳が連続した数字を見逃しているためだと思っていました。


ですから、私の質問は次のとおりです。出力の最初の「半分」で数行が失われる原因は何ですか? たとえば、「thread 3 setting threadInt to 84」という行はどこにありますか?

4

2 に答える 2

2

MSDN は、キーワードvolatileここで何をするかを説明しています:

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

これは、あなたの例では、これについて何が起こるかを意味します(これは時々変わる可能性があり、スケジューラによって異なります):

  • スレッド 0 が文字列を読み取っていますstaticVolatileTestString
  • スレッド 0 が「threadInt を 88 に設定するスレッド 0」を追加しています
  • スレッド 0 は文字列を書き戻していますstaticVolatileTestString

これはこれまでのところ予想どおりですが、次のようになります。

  • スレッド 1 ~ 4 は文字列を読み取っていますstaticVolatileTestString
  • スレッド 1 は、「threadInt を 97 に設定するスレッド 1」を追加しています。
  • スレッド 2 は、「threadInt を 11 に設定するスレッド 2」を追加しています。
  • スレッド 2 は文字列を書き戻していますstaticVolatileTestString
  • ...スレッド1、2、3は読み取りと追加、および書き込みです
  • スレッド 4 は文字列を書き戻していますstaticVolatileTestString
  • 等々...

ここで何が起こったのですか?スレッド 4 は文字列 'thread 0 setting threadInt to 88' を読み取り、その 'thread 4...' を追加して書き戻し、スレッド 1、2、および 3 が既に文字列に書き込んだすべてのものを上書きしました。

于 2012-06-12T09:28:25.043 に答える