1. スレッドの概要(2/2):プロセスとスレッドの違い
1.2 マルチプロセスとの違い
先の例で、シングルとマルチのプログラム上の違いについてはなんとなく判っていただけたかと思います。ではマルチプロセスとマルチスレッドにはどの様な違いがあるのでしょうか。
1.2.1 マルチプロセスの特徴
1.2.1.1 メモリ空間が独立している
プロセスが独立しているという事は、他のプロセスとのメモリや資源が独立して存在しているという事になります。よってグローバル変数やスタティック変数等の競合を心配する事無く使用できる利点があります。
逆に複数プロセスでメモリを共有する必要がある場合、プロセス間通信を自前で用意し運用する必要があり、プロセス間の排他制御・同期制御が煩雑になるという欠点を内包しています。
1.2.1.2 プロセス起動数が環境へ与える影響
一般的に子プロセスは自分が死んだ事を親プロセスへシグナルによって通知します。親プロセスは子プロセスの資源を回収するための処置を行う必要がありますが、それを行わない事とゾンビプロセスが発生し、システムに多大な影響を与えてしまいます。そのような実装を行っていたとしても、過度にプロセスを起動する事で同様の事象が発生します。状況によってはlsコマンドやpwdコマンドすら受け付けなくなり、最悪再起動しか手がない状況にも陥ります。
1.2.1.3 使用できる関数に制限がない
当然ですが通常使用している関数を普通に利用できます。
1.2.2 マルチスレッドの特徴
1.2.2.1 スレッド固有情報
マルチスレッドではスレッドが利用するスタックが独立しており、それ以外のメモリは基本的には他のスレッドおよび親プロセスから独立していません。具体的に言うと、スレッドが独立で管理できる情報、つまりスレッド固有なデータは(初期状態では)下記のみです。
- スタック
- スレッドID (
pthread_t
) - シグナルマスク (
pthread_sigmask(3)
) ※ signalについて 参照 - 代替シグナルスタック (
sigaltstack(2)
) ※ signalについて 参照 - errno 変数
- スケジューリングのポリシーと優先度 ※ 別章参照
グローバル変数などをスレッド間で共有できるため、プロセス間通信に代表されるような排他・同期処理の実装が容易で、結果的には高速に動作できます。逆に容易に共有できてしまうために不本意な書き換えやデッドロックなどの危険性が存在する事を常に意識する必要があります。
1.2.2.2 ゾンビプロセスが発生しない
マルチスレッドはあくまでプロセスが管理しているリソースに過ぎません。よってプロセスが終了すれば、そのプロセスが生成したスレッドも消滅します。
1.2.2.3 スレッドセーフな関数の考慮が必須
スレッドセーフな関数とは、「複数のスレッドが同時に呼び出しても問題ない関数のこと」です。次のどちらかの性質を満たしています。
- 関数内のスタティック変数やグローバル変数の操作をしない。
- 他の非スレッドセーフな関数を呼んでいない。
- そのような変数の操作をする際、その部分をmutexなどで同期化し、複数のスレッドが同時には操作しないように制限している。
C/C++で提供されている関数によっては内部で静的変数を抱えているものも少なくありません。使用したい標準関数がスレッドセーフであるか調査が必要です。
ココは非スレッドセーフであるものの一覧で、掲載されていない関数はマルチスレッド環境で問題なく使用できると期待できます。さらに、いくつかの非スレッドセーフ関数には、スレッドセーフ化した代用関数が用意されています。そのような関数には関数名として _r というサフィックスが付いており、区別できるようになっています。一覧はココにあります。
1.2.3 利点・欠点
マルチスレッドとマルチプロセスの1番の違いは、使用できるメモリ量だと思います。その点から、傾向としてマルチプロセスは大きなデータを扱う重い処理が得意、マルチスレッドは小さなデータを大量にさばく軽い処理が得意、という区分けができると思います。
マルチスレッドはマルチプロセスと比べると比較的新しい技術で、世の中の特にサーバプログラムはマルチプロセスで実装されている物が多いです。しかしマルチスレッドはマルチプロセスに比べて実装および資源の面で軽量で、且つパフォーマンス的にも魅力的な選択肢です。
また、マルチスレッドで書かれたアプリケーションをマルチプロセスへ移行するのは簡単で安全です。しかし逆は危険で、移植はまず無理と考えた方がよいでしょう。
以上から、マルチプロセスとマルチスレッドの特徴を理解して、場面に応じて適切に使用していくべきでしょう。