マルチスレッドのプログラミング

マルチスレッドの概念

この節では、マルチスレッドの基本概念について説明します。

並行性と並列性

マルチスレッドプロセスがシングルプロセッサ上で動作する場合は、プロセッサが実行リソースを各スレッドに順次切り替えて割り当てるため、プロセスの実行状態は並行的になります。この並行性は、複数のスレッドが進行しているが、実際にはこれらのスレッドが同時には実行されていないことを示しています。スレッド間の切り替えが高速なため、スレッドが同時に実行されているように見える可能性があります。

同じマルチスレッドプロセスが共有メモリー方式のマルチプロセッサ上で動作する場合は、プロセス中の各スレッドが別のプロセッサ上で実行されるため、プロセスの実行状態は並列的になります。これが本来の同時実行です。プロセス内のスレッド数が利用可能なプロセッサ数に等しいか、それより少ない場合は、オペレーティングシステムのスレッドサポートシステムによって、各スレッドがそれぞれ別のプロセッサ上で実行されることが保証されます。たとえば、4 つのスレッドを使用してプログラミングされ、2 つのデュアルコアプロセッサを備えたシステム上で実行される行列の乗算では、結果の行を同時に計算するために 4 つのプロセッサコア上で各ソフトウェアスレッドを同時に実行できます。

マルチスレッドの構造

従来の UNIX でもスレッドという概念はすでにサポートされています。各プロセスに単一のスレッドが含まれるので、複数のプロセスを使うようにプログラミングすれば、複数のスレッドを使うことになります。しかし、1 つのプロセスは 1 つのアドレス空間でもあるので、1 つのプロセスを生成すると 1 つの新しいアドレス空間が作成されます。

新しいプロセスを生成するかわりに、スレッドを生成するとシステムへの負荷は小さくなります。これは、新たに生成されるスレッドが現在のプロセスのアドレス空間を使用するからです。スレッドの切り替えに要する時間は、プロセスの切り替えに要する時間よりも短くて済みます。これは、スレッドを切り替える上でアドレス空間を切り替える必要がないからです。

同じプロセスに属するスレッド間の通信は簡単に実現できます。それらのスレッドは、もっとも重要なアドレス空間を含め、あらゆるものを共有しているからです。したがって、あるスレッドで生成されたデータを、プロセス中のほかのすべてのスレッドがただちに利用できます。

ただしこのようにデータを共有すると、プログラマにとって別の一連の課題が発生します。データが複数のスレッドから一度に変更されたり、あるスレッドによって変更されている間に別のスレッドから同時に読み取られたりすることのないようにスレッドを同期化する必要があります。詳細については、「スレッド同期」を参照してください。

ユーザーレベルのスレッド

スレッドは、マルチスレッドプログラミングにおける基本プログラミングインタフェースです。スレッドは、それらが存在するプロセスの内部からだけ参照でき、アドレス空間や開いているファイルなどすべてのプロセスリソースを共有します。

ユーザーレベルのスレッドの状態

スレッドごとに固有な状態としては次のものがあります。

スレッドはプロセスの命令とプロセスデータの大部分を共有します。このため、あるスレッドによって共有データに加えられた変更は、プロセス内のほかのスレッドから認識できます。スレッドが自分と同じプロセス内の他のスレッドとやり取りを行う場合は、オペレーティング環境を介する必要はありません。


注 –

ユーザーレベルのスレッドという呼称は、システムプログラマだけが関係するカーネルレベルのスレッドと区別するためのものです。このマニュアルは、アプリケーションプログラマ向けであるため、カーネルレベルのスレッドについては触れません。


スレッドのスケジューリング

POSIX 標準は、次の 3 つのスケジューリングポリシーを規定しています。 先入れ先出し (SCHED_FIFO)、ラウンドロビン (SCHED_RR)、そしてカスタム (SCHED_OTHER) です。 SCHED_FIFO は待ち行列ベースのスケジューラで、優先レベルごとに異なる待ち行列をもっています。SCHED_RR は FIFO に似ていますが、各スレッドに実行時間の制限があるという点が異なります。

SCHED_FIFOSCHED_RR は両方とも POSIX のリアルタイム拡張機能です。これらの方針で実行されているスレッドは、通常は特殊な特権が必要な Solaris のリアルタイム (RT) スケジューリングクラスに含まれます。SCHED_OTHER がデフォルトのスケジューリングポリシーです。SCHED_OTHER 方針で実行されているスレッドは、従来の Solaris タイムシェアリング (TS) スケジューリングクラスに含まれます。

Solaris は、ほかのスケジューリングクラス、つまり対話型タイムシェアリング (IA) クラス、公平分配 (FSS) クラス、および固定優先順位 (FX) クラスを提供しています。ここでは、これらの特殊なクラスについては説明していません。詳細は、Solaris priocntl(2) のマニュアルページを参照してください。

SCHED_OTHER 方針については、「LWP とスケジューリングクラス」を参照してください。

次の 2 つのスケジューリングスコープが使用できます。 プロセススコープ (PTHREAD_SCOPE_PROCESS) とシステムスコープ (PTHREAD_SCOPE_SYSTEM) です。スコープの状態が異なるスレッドが同じシステムに同時に存在でき、さらに同じプロセスにも同時に存在できます。プロセススコープは、同一プロセス内の別スレッドとだけリソースの奪い合いを行うスレッドに影響を及ぼします。システムスコープは、システム内のその他のすべてのスレッドとリソースの奪い合いを行うスレッドに影響を及ぼします。ただし、Solaris 9 リリースから、これらの 2 つのスコープの差異は事実上なくなっています。

スレッド取り消し

スレッドは、そのプロセス中のほかのスレッドの実行終了を要求できます。取り消しの対象となるスレッドは、取り消し要求を保留しておき、取り消し要求に応じるときにアプリケーション固有のクリーンアップを実行できます。

pthread 取り消し機能では、スレッドの非同期終了または遅延終了が可能です。非同期取り消しはいつでも起こりえます。遅延取り消しは定義されたポイントでのみ発生します。遅延取り消しがデフォルトタイプです。

スレッド同期

同期を使用すると、並行的に実行されているスレッドに関して、プログラムの流れと共有データへのアクセスを制御することが可能になります。

相互排他ロック (mutex ロック)、読み取り/書き込みロック、条件変数、セマフォーという 4 つの同期モデルがあります。