1

私が大学で取ったコースで覚えているのは、競合状態の私のお気に入りの例の 1 つは、単純なmain()メソッドが 2 つのスレッドを開始し、そのうちの 1 つは共有 (グローバル) 変数を 1 ずつインクリメントし、もう 1 つはそれをデクリメントするというものでした。擬似コード:

static int i = 10;

main() {
    new Thread(thread_run1).start();
    new Thread(thread_run2).start();
    waitForThreads();
    print("The value of i: " + i);
}

thread_run1 {
    i++;
}

thread_run2 {
    i--;
}

次に、教授はi、100 万億回実行した後の の値を尋ねました。(本質的に 10 以外になるとしたら。) マルチスレッド システムに慣れていない学生は、100% の確率で、このprint()ステートメントは常にi10 と報告されると答えました。

私たちの教授は、各インクリメント/デクリメント ステートメントが実際には 3 つのステートメントとして (アセンブリに) コンパイルされたことを示したため、これは実際には正しくありません。

1: move value of 'i' into register x
2: add 1 to value in register x
3: move value of register x into 'i'

したがって、 の値はi9、10、または 11 になる可能性があります (詳細には触れません)。

私の質問:

物理レジスタのセットがプロセッサ固有であることは、私の理解でした(そうですか?)。デュアル CPU マシンで作業する場合 (デュアルコアとデュアル CPU の違いに注意してください)、各 CPU には独自の物理レジスタ セットがありますか? 私は答えがイエスだと思っていました。

シングル CPU (マルチスレッド) マシンでは、コンテキスト切り替えにより、各スレッドが独自の仮想レジスタ セットを持つことができます。デュアル CPU マシンには 2 つの物理的なレジスタ セットがあるため、競合状態が発生する可能性がさらに高まるのではないでしょうか。 CPUマシン?(コンテキストスイッチごとにレジスタの状態を退避・復帰することを参考にした仮想同時動作。)

より具体的に言えば、これを 8 CPU マシンで実行した場合、各 CPU に 1 つのスレッドがあり、競合状態は解消されますか? この例を拡張して、各 CPU に 4 つのコアを持つデュアル CPU マシンで 8 つのスレッドを使用する場合、競合状態の可能性は増加しますか、それとも減少しますか? オペレーティング システムは、アセンブリ命令が 2 つの異なる CPU で同時に実行されるのをどのように防いでいますか?step 3

4

3 に答える 3

1

はい、デュアルコア CPU の導入により、潜在的なスレッド競合を伴うかなりの数のプログラムがすぐに失敗しました。シングルコア CPU は、スケジューラがスレッド間でスレッド コンテキストを迅速に切り替えることによってマルチタスクを実行します。これにより、古い CPU キャッシュに関連するスレッド化バグのクラスが解消されます。

ただし、単一のコアでも失敗する可能性があります。変数の値をインクリメントするためにレジスターにロードしたときに、スレッド・スケジューラーがスレッドを中断したとき。スケジューラがスレッドを中断する可能性がそれほど高くないため、それほど頻繁に失敗することはありません。

オペレーティング システムには、これらのプログラムが数分以内にクラッシュするのではなく、いずれにせよぐったりすることを許可する機能があります。「プロセッサ アフィニティ」と呼ばれ、Windows の start.exe の AFFINITY コマンド ライン オプション、winapi の SetProcessAfinityMask() として使用できます。変数をアトミックにインクリメントおよびデクリメントするヘルパー メソッドについては、Interlocked クラスを確認してください。

于 2011-04-03T17:53:06.037 に答える
1

あなたはまだ競合状態を抱えているでしょう - それはまったく変わりません。両方が同時にインクリメントを実行する 2 つのコアを想像してみてください。両方とも同じ値をロードし、同じ値にインクリメントしてから、同じ値を格納します...したがって、2 つの操作による全体的なインクリメントは 2 ではなく 1 になります。 .

メモリ モデルが関係する潜在的な問題の追加の原因があります。ステップ 1で の最新の値が実際にi取得されず、ステップ 3 で の新しい値がi他のスレッドから見える方法ですぐに書き込まれない場合があります。

基本的に、それはすべて非常にトリッキーになります。そのため、共有データにアクセスするときに同期を使用する、何をしているかをよく知っている専門家によって書かれたロックフリーの高レベルの抽象化を使用するのが一般的に良い考えです。

于 2011-04-03T17:40:00.027 に答える
1

第 1 に、デュアル プロセッサとデュアル コアは実質的な効果がありません。デュアル コア プロセッサでも、チップ上に 2 つの完全に独立したプロセッサがあります。一部のキャッシュを共有し、メモリ/周辺機器への共通バスを共有する場合がありますが、プロセッサ自体は完全に分離されています。(ハイパースレッディングなどのデュアルスレッドの単一コード) は 3 番目のバリエーションですが、仮想プロセッサごとに一連のレジスタも備えています。2 つのプロセッサは実行リソースの 1 つのセットを共有しますが、完全に別個のレジスタ セットを保持します。

第 2 に、本当に興味深いケースは 2 つしかありません。単一スレッドの実行と、それ以外のすべてです。複数のスレッドがあると (すべてのスレッドが単一のプロセッサで実行されている場合でも)、数千のプロセッサを搭載した巨大なマシンで実行している場合と同じ潜在的な問題が発生します。さて、コードがより多くのプロセッサ (作成したスレッドと同じ数まで) で実行されると、問題がより早く明らかになる可能性が高いことは確かですが、問題自体は発生していない/していない.全然変わります。

実用的な観点からは、より多くのコアを持つことは、テストの観点から役立ちます。典型的な OS でのタスク切り替えの粒度を考えると、単一のプロセッサで問題を示さずに何年も実行でき、さらに 2 つのプロセッサで実行すると数時間または数分でクラッシュして燃え尽きるコードを書くのは非常に簡単です。または物理プロセッサ。ただし、問題は実際には変わっていません。より多くのプロセッサを使用すると、より迅速に発生する可能性が高くなります。

最終的に、競合状態 (またはデッドロック、ライブロックなど) は、コードが実行されるハードウェアではなく、コードの設計に関するものです。関連する条件を適用するために実行する必要がある手順は、ハードウェアによって異なりますが、関連する違いは、単純なプロセッサ数とはほとんど関係ありません。むしろ、複数のプロセッサを搭載した単一のマシンではなく、完全に分離されたアドレス空間を持つ複数のマシンがある場合の譲歩のようなものです。そのため、メモリに値を書き込むときに、そのメモリを直接見ることができない他のマシンの CPU から見えるようになります。

于 2011-04-03T17:59:52.607 に答える