タスクマネージャーを開くとプロセス一覧が表示され、開発ドキュメントを読むとスレッドの話が出てきます。どちらも「実行される何か」のようですが、正確には何が違うのでしょうか。
技術面接で最もよく出る質問を一つ挙げるなら、間違いなくこれです。「プロセスとスレッドの違いを説明してください。」
定番の質問である理由は簡単です。この一問で、メモリ構造、OS、並行処理まで続けて確認できるからです。
この記事では、2つの概念の違いをメモリ構造の観点から整理し、面接でどこまで答えるとよいかまで解説します。
まずは要点から整理します。
- プロセス(Process):実行中のプログラム。独立したメモリ空間を丸ごと持つ
- スレッド(Thread):プロセス内の実行フロー。スタックだけを個別に持ち、それ以外は共有する
- 共有するためスレッドは軽量で高速ですが、同時にデータ競合も発生します
- プロセスは分離されて安全ですが、その分重く、通信も煩雑です
プロセス:実行中のプログラム
プログラムはディスクに保存されたコードの塊です。まだ何もしていません。
プログラムを実行した瞬間、OSはそれをメモリにロードし、CPUを割り当てる準備をします。この「実行中のプログラム」がプロセスです。
OSはプロセスごとに独立した仮想メモリ空間を丸ごと与えます。構造は大きく4つの領域に分かれます。
| 領域 | 格納するもの |
|---|---|
| コード(Text) | 実行する機械語コード |
| データ(Data) | グローバル変数、静的変数 |
| ヒープ(Heap) | 実行時に動的に割り当てるメモリ |
| スタック(Stack) | 関数呼び出し情報、ローカル変数 |
ここで重要なのは「独立」という言葉です。プロセスAはプロセスBのメモリを参照できません。OSが根本から遮断します。
そのため、あるプロセスが終了しても他のプロセスは正常に動き続けます。Chromeがタブごとに別プロセスを起動するのはこのためです。1つのタブがクラッシュしても、ブラウザ全体は動き続けます。
スレッド:プロセス内の実行フロー
スレッドは、プロセス内で実際にコードを実行するフローの単位です。すべてのプロセスは、少なくとも1つのスレッド(メインスレッド)を持って開始します。
ポイントは共有範囲です。同じプロセス内のスレッドはコード・データ・ヒープ領域を共有し、スタックだけをそれぞれ個別に持ちます。
スタックを個別に持つ理由は明確です。スタックは「今どの関数をどこまで実行しているか」の記録なので、実行フローごとに1つ必要だからです。
一方、ヒープを共有するため、スレッド同士は1つの変数を使ってすぐにデータを受け渡せます。別途通信手順は必要ありません。
なぜスレッドを使うのか:コストの問題
「複数の処理を同時に行いたいなら、プロセスを複数起動すればよいのでは?」と思うかもしれません。しかし問題はコストです。
生成コスト。プロセスを作るには独立したメモリ空間を丸ごと準備する必要があります。スレッドならスタックを1つ追加するだけです。
切り替えコスト。CPUが別のプロセスへ移るには、メモリマップ全体を入れ替える必要があります(コンテキストスイッチ)。同じプロセス内のスレッド切り替えははるかに軽量です。
通信コスト。プロセス間でデータをやり取りするには、パイプやソケットなどのIPC(プロセス間通信)手順が必要です。スレッドなら同じ変数を読むだけで済みます。
つまり、スレッドは低コストで複数の処理を動かすための単位です。
無料ではありません:データ競合
スレッドの利点はすべて「メモリを共有する」ことから生まれますが、最大の弱点もまったく同じ場所にあります。
2つのスレッドが同じ変数に同時に書き込むと、結果を予測できません。これがデータ競合です。
count += 1のような1行のコードも、内部では読み取り→加算→書き込みの3段階です。そのため2つのスレッドが重なると、実際にインクリメントが失われます。
そこでロックやセマフォなどの同期ツールが必要になりますが、使い方を誤るとデッドロックという別の地獄が始まります。
プロセスは最初からメモリが分離されているため、この問題がありません。安全性と効率のトレードオフというわけです。
iOS開発者の視点では
iOSでは、1つのアプリが1つのプロセスです。サンドボックスポリシーにより、アプリは他のアプリのメモリにアクセスできません。先ほど説明したプロセス分離がそのまま適用されています。
アプリ内ではメインスレッドがUIを担当し、ネットワーク処理や重い計算は別のスレッドに送ります。「UI更新はメインスレッドで」というルールは、まさにスレッドの概念に基づいています。
ただし、最近のiOS開発でスレッドを直接作ることはほとんどありません。GCDのキューやSwift ConcurrencyのTaskに処理を渡せば、システムがスレッドプールから自動的に割り当てます。抽象化の層が1つ上がっただけで、基盤でスレッドが動いている事実は変わりません。
面接ではこう答える
まず一文で答え始めるとよいでしょう。
「プロセスは独立したメモリ空間を持つ実行単位で、スレッドはプロセス内でスタックだけを個別に持ち、残りのメモリを共有する実行フローです。」
続く質問もほぼ決まっています。「スレッドは何を共有し、何を個別に持ちますか?」(スタックだけ)、「なぜスレッドは軽量なのですか?」(生成・切り替え・通信コスト)、「共有すると何が問題ですか?」(データ競合と同期)。ここまで答えられれば合格点です。
さらにChromeのタブやiOSのサンドボックスなど実例を1つ加えると、暗記ではなく理解しているという印象を与えられます。
まとめ
- プロセスは実行中のプログラムで、OSから独立したメモリ空間(コード・データ・ヒープ・スタック)を受け取る
- スレッドはプロセス内の実行フローで、スタックだけを個別に持ち、コード・データ・ヒープを共有する
- スレッドは生成・切り替え・通信コストが低く軽量だが、メモリ共有によりデータ競合の危険がある
- プロセスは分離されて安全だが重く、通信にはIPCが必要
- iOSではアプリは1つのプロセスで、GCDとTaskはスレッド上の抽象化です
- 面接回答の骨子:定義の一文 → 共有範囲 → コストの違い → データ競合

