「プロセスは実行中のプログラムを抽象化したものである」これは私が著書 "Linux Kernel Development" の中で使った比喩だ。バイナリイメージ、仮想メモリ、各種カーネルリソース、関連付けられたセキュリティコンテキストといったものだ。スレッドは、「プロセスの中の実行単位」で、仮想プロセッサやスタック、プログラムの状態だ。言い換えれば、プロセスは実行されるバイナリであり、スレッドはOSのプロセススケジューラがスケジューリングできる最小の実行単位であると言える。
プロセスは1つ以上のスレッドを持っている。シングルスレッドのプロセスでは、1つのプロセスは1つのスレッドしか持っていない。つまり、スレッドはプロセスであると言ってしまえる。実行されるのは何か1つのことだけだ。マルチスレッドのプロセスでは、プロセスは複数のスレッドを持っている。つまり、複数の何かが実行されている。
現代のOSにおける、主な仮想化された抽象化層といえば、仮想メモリと仮想プロセッサだ。どちらも、実行中のプロセスが、マシンのリソースを独り占めしているように見せかける仕組みを提供している。仮想メモリは、プロセスに対して、物理メモリあるいはディスク上のストレージ(スワップ領域)に対してシームレスに、一意なメモリ空間を割り当てる。仮想プロセッサは、実際には複数のプロセスが複数のプロセッサ上でマルチタスクをこなしているにもかかわらず、プロセスがそのシステム上で1つしか動いていないかのように振る舞わせることができる。
仮想メモリは、プロセスに対して関連づいているもので、スレッドに対してではない。そのため、スレッドは同じメモリアドレス空間を共有することになる。逆に言うと、仮想プロセッサは各スレッドに関連づいている。それぞれのスレッドは独立してスケジュールできる要素だ。
それで、何がポイントだろうか? プロセスは明らかに必要なものだ。しかし、なぜスレッドという別のコンセプトが現われ、マルチスレッドのプロセスというのが使えるようになったのか? これは、マルチスレッドの基本的な4つの利点があるからだ。
プログラミングの抽象化 タスクを分けて、それぞれを実行単位(つまりスレッド)に割り当てるのは、多くの問題において自然なアプローチだ。このアプローチを活用したプログラミングパターンには、reactorやthread-per-connection、thread poolパターンがある。一方でスレッドをアンチパターンだという人もいる。比類なきプログラマ、アラン・コックスは「スレッドはステートマシンを理解できない人のためのものだ」と言っている。
並列性 プロセッサを複数持つマシンでは、スレッドは 真の並列性 を実現するための効率的な方法である。各スレッドは自分の仮想プロセッサを持ち、独立してスケジューリング可能な要素であるため、複数のスレッドは同時に複数のプロセッサ上で実行することができ、システムのスループットを上げることが可能になる。スレッドが並列性の実現のために使われているという意味においては、スレッドはプロセッサ数よりも少ない数が実行されているということになる。ここでは、先の「スレッドはステートマシンを理解できない人のためのもの」というのは適用されない。
I/Oのブロッキング スレッドなしでは、I/Oのブロッキングによってプロセス全体が止まってしまう。これは、スループットとレイテンシの両方にとって有害だ。マルチスレッドなプロセスでは、それぞれのスレッドがブロックされ、I/O待ちになっても、一方では他のスレッドが処理を先に進める。非同期・ノンブロッキングI/Oは、この問題に対するスレッドに変わる解決法の1つだ。
メモリの節約 スレッドは、メモリを共有する効率的な方法を提供する一方で、複数の実行単位を最適化する。この意味では、マルチプロセスの代わりであるといえる。
これらの利点によるコストは、ミューテックスや状態変数のような仕組みを通して同時実行を管理する必要があるという形で複雑性が増すことだ。マルチコアプロセッサやマルチプロセッサのマシンが一般的になる傾向が進むとすると、スレッドはシステムプログラミングに置いてより重要なツールになっていくだろう。