シングルプロセッサで(プリエンプティブ)マルチスレッドモデルを使用してプログラミングする場合、同期やデッドロックなどに対処する必要があります。複数のプロセッサでマルチスレッドに切り替えるときに、追加の懸念事項(およびそれに応じて使用される新しい手法)はありますか?
基本的に、他の問題はキャッシュの一貫性です。メモリを使用して通信するスレッドは、最新のアーキテクチャで潜在的な問題を抱えています。
シングルコアプロセッサにはキャッシュが1つしかないため、常に一貫しています。つまり、メモリを介してスレッド間で情報を共有するための通常の競合状態とデッドロック(ロック周辺)のみがあります。
マルチコアプロセッサには通常、CPUごとに個別のキャッシュ領域があります。これは、異なるCPUのキャッシュが不整合になる可能性があることを意味します。これは、スレッド間で情報を共有する際の問題のもう1つの領域です。これは memory models のトピックで説明されています。
x86とx64は非常に強力なメモリモデルを備えているため、揮発性の宣言を忘れたり、適切なフェンスやバリアを使用したりするプログラマには寛容です(たとえば、C/C++標準で要求されているとおり)。 (FYI、強力なメモリモデルは実装に手間がかかるため、消費電力が増加し、パフォーマンスが低下します。)
マルチコアを備えた他のハードウェアアーキテクチャ(特にMIPSなどのRISCアーキテクチャ)は、キャッシュの一貫性を維持するための正しい処理を行うソフトウェアに依存しています。ソフトウェアはフェンスとバリアを使用して、CPU間で絶え間なく変化するメモリについて何が重要かを伝達する必要があります。
弱いメモリモデルの問題は、JavaやC#ランタイムなど)の上位レベルのソフトウェアによって隠される場合がありますが、この領域は悲しいことに複雑であり、アルゴリズムに微妙なバグが存在することがよくあります。
メモリモデルの概念は、特定の条件が検出されたときにプロセッサがメインメモリから再ソーシングすることにより、キャッシュに再ロードする必要があるものに適用されます。すべての条件が原因でキャッシュのリロードが発生し、プロセッサーの速度が低下する場合、ソフトウェアの観点からは、有用な理由がない場合がよくあります。問題は、他のCPUと共有するために重要なメモリ変更と重要でないメモリ変更を決定することです。したがって、アーキテクチャが異なると、トレードオフも異なります。
余談ですが、シングルコアとして使用しているカスタムエンベデッドシステムでは、一連の命令内で中断されたかどうかを判断する機能を作成できます(たとえば、中断カウントを保持し、開始時にキャプチャして終了時に比較するか、または単に代わりに割り込みをオフにすることにより)。一連の命令の中で中断されていない場合、ソフトウェアはそれがアトミックに機能していると想定できます。これらの手法は、マルチコアプロセッサでは機能しません(同じ保証はありません)。
ロックが解決する問題は、マルチプロセッシング(つまり、2つのスレッドが異なるコアで同時に実行される)によって引き起こされるのではなく、原子性の欠如によって引き起こされます。
だから私たちが言うとき:
lock (lockObject)
{
// This code is atomic...
}
...そのクリティカルセクションに入るスレッドは、現在のスレッドの実行が完了するまで待機する必要があるためです。これは、複数のコアが動作しているかどうかに関係なく当てはまります。
マルチプロセッシングは、ハードウェア割り込みを備えたシングルコアマシンで忠実にシミュレーションされます。複数のコアが使用されている場合、通常の同時実行技術ではまだ解決されていない新しい問題は発生しません。