2

次のインタビューの質問があります。

class someClass
{
    int sum=0;
    public void foo()
    {
        for(int i=0; i<100; i++)
        {
            sum++
        }
    }
}

foo メソッドを介して実行される 2 つの並列スレッドがあります。最後の sum の値は 100 から 200 まで変化します。問題はその理由です。私が理解しているように、1 つのスレッドのみが CPU を取得し、スレッドは実行中にプリエンプトされます。外乱によって合計が 200 にならないのはどの時点ですか?

4

3 に答える 3

10

インクリメントはアトミックではありません。値を読み取り、インクリメントされた値を書き出します。これら 2 つの操作の間に、もう一方のスレッドが複雑な方法で合計を操作できます。

値の範囲は、実際には 100 から 200 ではありません。この範囲は、スレッドが交代するか、各読み取りを同時に実行するという誤った仮定に基づいています。さらに多くの可能なインターリーブがあり、そのうちのいくつかは著しく異なる値を生成します。最悪のケースは次のとおりです (x式で使用される暗黙的な一時を表しますsum++)。

    Thread A            Thread B
----------------    ----------------
x     ← sum (0)
                    x     ← sum (0)
x + 1 → sum (1)
x     ← sum (1)
x + 1 → sum (2)
⋮
x     ← sum (98)
x + 1 → sum (99)
                    x + 1 → sum (1)
x     ← sum (1)
                    x     ← sum (1)
                    x + 1 → sum (2)
                    ⋮
                    x     ← sum (99)
                    x + 1 → sum (100)
x + 1 → sum (2)

したがって、可能な最小値は 2 です。簡単に言えば、2 つのスレッドは互いのハードワークを元に戻します。スレッド B はスレッド A にゼロをフィードできないため、2 を下回ることはできません。スレッド A は、インクリメントされた値しか出力できません。スレッド A は、スレッド B が与えた 1 をインクリメントする必要があります。

于 2011-11-17T12:30:33.573 に答える
3

sum++は競合状態です。

両方のスレッドは、sum の値を 0 として読み取ることができ、それぞれがその値を 1 にインクリメントして、その値を元に戻します。これは、sum の値が 2 ではなく 1 になることを意味します。

このように続けると、100 から 200 の間の結果が得られます。

于 2011-11-17T11:45:51.207 に答える
0

現在、ほとんどの CPU には複数のコアがあります。そのため、関数 foo() の先頭でミューテックスをロックし、for ループが終了した後にミューテックスをロック解除すると、異なるコアで実行されている 2 つのスレッドは依然として答え 200 を返します。

于 2011-11-17T22:04:25.320 に答える