プロセス内に複数の「実行の流れ」を作るスレッドの仕組みと、その落とし穴(レースコンディション)を可視化します。
スレッド(Thread)は、1つのプロセスの中で並列に動く「実行の流れ」のことです。プロセスの「メモリ空間(コード・データ・ヒープ)」を全スレッドで共有しながら、各スレッドは自分のスタックだけ独自に持ちます。
身近な例えだと、「1つの台所で複数の料理人が働く」イメージです。料理人(スレッド)は同じ冷蔵庫や調味料棚(共有メモリ)を使えますが、自分の作業スペース(独自スタック)は別々に持っています。これにより材料を取り合うこともなく効率的に並列作業できる ──ただし、同じ砂糖入れに同時に手を伸ばすと事故(後述のレースコンディション)が起きます。
上のツール「スレッドの基本構造」を ▶ 自動再生すると、1プロセス内にスレッドが増えていく様子を確認できます。
| 項目 | プロセス | スレッド |
|---|---|---|
| メモリ空間 | 独立 | 共有(スタックのみ独自) |
| 生成コスト | 重い | 軽い |
| コンテキストスイッチ | 重い | 軽い |
| 通信 | プロセス間通信(IPC)が必要 | 共有メモリで直接 |
| 独立性 | 高(1つ落ちても他は無事) | 低(1つ落ちるとプロセスごと落ちる) |
プロセスとスレッドは「並列処理の実行単位」という点で似ていますが、性質が大きく違います。独立性とパフォーマンスのトレードオフと覚えると整理しやすいです。
・独立性が欲しいとき:プロセス(プロセス間で影響しない)
・性能・通信効率が欲しいとき:スレッド(共有メモリで高速)
例えばWebブラウザは、タブごとを別プロセスにすることで「1つのタブが固まっても他は無事」を実現しています。一方、ブラウザのGUI描画はマルチスレッドで動かして、操作を滑らかに保っています。
スレッドの大きな利点は「メモリ共有で簡単に協調作業できる」点です。プロセスでは別プロセスとのデータ交換に専用の仕組み(パイプ・ソケット・共有メモリAPI)が必要ですが、スレッド同士ならただの変数でやり取りできます。
メモリ共有のメリット:
・生成が高速:メモリ空間をコピーする必要がない
・通信が高速:変数の読み書きだけで情報交換できる
・メモリ効率が良い:複数スレッドで同じデータを共有
この特性により、Webサーバー(リクエストごとに1スレッド)、ゲームエンジン(描画・物理・AIを並列化)、数値計算(同じ配列を複数スレッドで処理)など、性能が要求される場面で広く使われます。
メモリ共有は便利ですが、2つのスレッドが同じ変数を同時に更新すると、結果が壊れることがあります。これをレースコンディション(race condition)と呼びます。
典型的な発生パターン:
・スレッドA が変数 x の値(例: 0)をレジスタに読む
・OSがスレッドB に切替。B も x を読む(まだ 0)
・B が x = 0 + 1 = 1 を計算して書き戻す
・A が再開し、レジスタの古い値で x = 0 + 1 = 1 を書き戻す
・本来は 2 回 +1 されて x = 2 のはずが、1 にしかなっていない
対策には排他制御(ロックやセマフォを使って同時アクセスを禁止する)が必要です。レースコンディションはタイミング次第で発生するため、テストで再現しにくい厄介なバグの代表格です。上のツール「レースコンディションの発生」シナリオで一連の流れを確認できます。
プロセス切替では仮想メモリ管理情報(ページテーブル)まで入れ替える必要がありますが、同一プロセス内のスレッド切替ではメモリ管理情報は同じままでよいため、レジスタとスタックポインタの退避・復元だけで済みます。
数字感としては、プロセスのコンテキストスイッチが数マイクロ秒かかるのに対し、スレッドのコンテキストスイッチは数百ナノ秒程度。10倍前後の差があります。
そのため、頻繁に切替が必要な並列処理(Webサーバーの1リクエスト=1スレッド方式など)ではスレッドが採用されます。「スレッドは軽量、プロセスは重い」と覚えておくとよいでしょう。
マルチスレッドは身近なソフトウェアで広く使われています。
・Webサーバー:1リクエストごとに1スレッドを割り当てて、複数のクライアントに同時応答(Apache のスレッドモデル)
・GUI アプリ:画面描画スレッドと裏で動く処理スレッドを分離し、操作中もアプリが固まらないようにする
・科学計算 / 機械学習:行列演算を複数スレッドに分散して、CPU の全コアを使い切る
・ゲームエンジン:描画・物理シミュレーション・AI・サウンドをスレッド分離
どれも「並列にやることで速くなる」ケースですが、レースコンディションを避けるためのロック設計が腕の見せ所になります。
シングルスレッド(=実行の流れが1本だけ)のプログラムでは、1つの処理が終わるまで次の処理が始められません。ファイルの読み込みやネットワーク通信のような待ち時間の長い処理が発生すると、その間アプリ全体が止まってしまいます。
なぜスレッドが必要か。ダウンロード中でも画面操作ができる、音楽を聴きながら別の作業ができる——これらはすべて複数の処理を同時に進めたい要求から来ています。マルチスレッドにすることで、1つのスレッドが待っている間に別のスレッドが動き続けられるため、「アプリが固まる」問題を解消できます。
身近な例では、料理のながら作業が近いイメージです。お湯を沸かしながら(待機中のスレッド)、その間に野菜を切る(別のスレッド)ことで、全体の調理時間を短くできます。シングルスレッドだと「お湯が沸くまで包丁を持てない」状態になってしまいます。スレッドはこの「ながら作業」を実現するための仕組みです。