プロセスとスレッド — 共有メモリは速いが危ない
プロセスは、それぞれが独立した「個室オフィス」です。専用のメモリ(机)を持ち、隣のプロセスの机は勝手に覗けません。だから安全ですが、データのやり取りには郵送のような手間がかかります。
一方 スレッドは、同じオフィスの中で1つの机を共有して働く同僚たち。メモリを共有しているので連携は高速です。でも、同じ机(共有データ)に同時に手を伸ばすと事故が起きます。これが競合状態(レースコンディション)。下の図1で、2つのスレッドに共有カウンターを増やさせて、何が起きるか見てください。
なぜ +1 が消えるのか
count = count + 1 は一見ひとかたまりですが、CPUの中では「①共有メモリから読む → ②自分のレジスタで+1 → ③書き戻す」の3手順に分かれます。ロック無しだと、スレッド1が読んだ直後にスレッド2も同じ古い値を読んでしまうことがあります。すると両者が「0を読んで1を書く」を別々にやり、本来2になるはずが1にしかならない。これが失われた更新(ロスト更新)です。図1のロック「なし」で再現できます。
防ぐには、この3手順を「誰にも割り込ませない1つのかたまり(不可分・アトミック)」にします。それがロック(ミューテックス)。あるスレッドが読み始めたら書き戻すまで鍵をかけ、ほかは順番待ち。図1のロック「あり」では必ず 4 になります。ただし順番待ちが発生するぶん遅くなり、待ち合わせを間違えるとデッドロックの危険も。共有メモリは速さと引き換えに、こうした注意を要求するのです(プロセスはメモリを共有しないので、そもそもこの競合は起きません)。
- プロセス
- 独立したメモリ空間を持つ実行単位。互いのメモリは原則見えない(隔離)。
- スレッド
- 同じプロセス内でメモリを共有する実行単位。連携は速いが競合に注意。
- 競合状態
- 複数スレッドが共有データに同時アクセスし、実行順しだいで結果が壊れること。
- ロック
- 共有データへの操作を1スレッドずつに制限する仕組み(ミューテックス)。
まとめ
プロセスは「メモリを共有しない安全な個室」、スレッドは「メモリを共有する高速な相部屋」。スレッドの速さは共有メモリから来ますが、その代償が競合状態です。count+1 すら3手順に割れて割り込まれる——図1で見たロスト更新は、共有データの読み書きはロックで不可分にまとめる必要があることを教えてくれます。並行処理の事故の多くは、この一点から生まれています。