各スレッドは独自のスタックと,独自の CPU レジスタのコピーを持っています。ファイル,装置,スタティック・データ,およびヒープ・メモリーなどの他のリソースは,プロセス内のすべてのスレッドで共有されます。これらの共通リソースを使用するスレッドは,互いに作業の調整を行わなくてはなりません。リソースを同期する方法はいくつかあります。
クリティカル・セクションとは,共有不可能なリソースを参照するコードのブロックです。クリティカル・セクションは,一般に,一度に 1 つのプロセス内のスレッドからしか使用できないデータまたはコードの参照を制約するために使用されます (たとえば,共通ブロック内の共有データの変更など)。
ミューテックスは,一度に 1 つのスレッドだけがリソースを参照できるようにする機構です。ミューテックスは一般に,一度に 1 つのスレッドからしか使用できないシステム・リソース (プリンタなど) の参照を制約するときか,共有によって予期しない結果が生じる可能性があるときに使用されます。
セマフォは,リソースを使用できるスレッドの数を規制するカウンターです。セマフォは一般に,同一のリソースの参照を一定数に抑えるために使用されます。
イベントとは,1 つまたは複数のスレッドに対して,何らかのイベントが発生したことを知らせるオブジェクトです。
これらのオブジェクトの状態は,シグナル状態と非シグナル状態のどちらかです。シグナル状態は,そのリソースがプロセスまたはスレッドから利用できる状態にあることを示します。非シグナル状態は,リソースが使用中であることを示します。
次の節で説明するルーチンは,リソース共有機構の作成,初期化,および終了を管理します。その中には,状態を非シグナル状態からシグナル状態に変えるものがあります。WaitForSingleObject と WaitForMultipleObjects ルーチンもオブジェクトのシグナル状態を変更します。これらの関数についての詳細は,「スレッドの同期」を参照してください。
この節では,以下のことも説明しています。
Win32 スレッドの調整と同期に関する参考資料は,「その他の情報」を参照してください。
クリティカル・セクションを使ってスレッドを同期する前に,InitializeCriticalSection を呼び出し,別のスレッドから参照される大域変数または共通ブロックのアドレスを渡して初期化を行わなくてはなりません。大域変数の処理を開始するときには EnterCriticalSection を呼び出し,アプリケーションが処理を終えたら LeaveCriticalSection を呼び出します。EnterCriticalSection と LeaveCriticalSection は,どちらもアプリケーションの中で何度でも呼び出せます。クリティカル・セクションを使用するマルチスレッドの Visual Fortran サンプルについては,PEEKAPP.F90 を参照してください。
MUTual EXclusion オブジェクト (ミューテックス)
CreateMutex でミューテックス・オブジェクトが作成されます。この関数は,そのミューテックスがすでに存在している場合 (別のプロセスまたはスレッドによって同じ名前のミューテックスが作成されていた場合) にはエラーを返します。CreateMutex を呼び出した後に GetLastError を呼び出して,エラー状態 ERROR_ALREADY_EXISTS が発生しているかどうかを確かめてください。また,OpenMutex 関数を使って,その名前のミューテックス・オブジェクトがすでに存在しているかどうかを調べることもできます。OpenMutex は呼び出されると,オブジェクトが存在している場合,そのハンドルを,指定された名前のミューテックスが発見できなかった場合,ヌルを返します。OpenMutex を使っても,ミューテックス・オブジェクトの状態がシグナル状態に変わることはありません。シグナル状態を変えるのは,「スレッドの同期」で説明しているいずれかの待機ルーチンです。
ReleaseMutex は,ミューテックスを非シグナル状態からシグナル状態に変更します。この関数は,呼び出し側のスレッドがそのミューテックスを所有している場合にのみ効果を持ちます。ミューテックスがシグナル状態になっていれば,それを待っている任意のスレッドがミューテックスを獲得して,実行を開始することができます。
セマフォを処理するための関数は,ミューテックスを管理する関数とほぼ同じです。CreateSemaphore は,リソースを参照できるスレッドの数の初期値と最大値を指定して,セマフォを作成します。OpenSemaphore は,OpenMutex と同様に,指定されたセマフォ・オブジェクトが存在していれば,そのハンドルを返します。このハンドルは,ハンドルを必要とする任意の関数で使用できます (「スレッドの同期」で説明している待機関数など)。OpenSemaphore を呼び出しても,リソースの使用可能なカウンターが減るわけではありません。カウンターを減らすのは,リソースを待っている関数です。
ReleaseSemaphore を使うと,リソースの使用可能なカウンターが指定された数だけ増えます。この関数は,スレッドがリソースの処理を終えたときに呼び出します。もう 1 つの用途は,初期カウンターとして 0 を指定して CreateSemaphore を呼び出し,初期化プロセス間にリソースが参照されるのを禁止するというものです。アプリケーションが初期化を終えたら,ReleaseSemaphore を呼び出して,リソースのカウンターをその最大値まで増やします。
イベント・オブジェクトは他のスレッドの実行をトリガすることができます。イベントは,あるスレッドが別の複数のスレッドにデータを提供しているような場合に使用できます。イベント・オブジェクトを作成するには,CreateEvent 関数を使用します。作成側のスレッドは,オブジェクトの初期状態と,それが手動リセット・イベントと自動リセット・イベントのどちらであるのかを指定します。手動リセット・イベントは,ResetEvent への呼び出しによって明示的にリセットされるまではシグナル状態のままになるイベントです。自動リセット・イベントは,シグナルを待っているスレッドが解放されたときに,自動的にシステムによってリセットされます。
イベント・オブジェクトをシグナル状態にするには,SetEvent か PulseEvent を使用します。OpenEvent は,イベントへのハンドルを返します。このハンドルは他の関数呼び出しで使用することができます。ReleaseEvent は,イベントの所有権を解放します。
各スレッドは独自のスタックを持っているので,使用するスタティック・データをできるだけ少なくすることで,データ項目をめぐって衝突が起こる可能性を減らすことができます。プログラムを設計するときには,スレッドに対して固有になるすべてのデータで,自動スタック変数を使用するようにしてください。マルチスレッド・ルーチン中で宣言されたすべての変数は,基本設定ではスタティックになり,スレッド間で共有されます。スレッドが他のスレッドが使用している変数を上書きしないようにする方法としては,次のものがあります。
変数を AUTOMATIC として宣言します。
スレッドごとに変数値のベクトルを 1 つ作成し,異なるスレッドで使用される変数値が異なる記憶位置に置かれるようにします。CreateThread によって渡される 1 つの整数パラメタを,スレッドを識別する索引として使用することができます。
スレッド・ローカル・ストレージ (TLS) を使用します。
自動変数として宣言された変数は,スレッドとともに保存されるスレッド・コンテキストの一部であるスタックに置かれます。手続内の自動変数は,手続が実行を完了した時点で破棄されます。
ファイルと装置はスレッド間で共有されますが,これらの共有リソースのスレッドによる使用を調整しなくてすむ場合があります。Fortran は個々の入出力文をアトミックな操作として扱います。2 つの異なるスレッドが同じ装置に対して書き出しを試み,1 つのスレッドの出力操作が開始された場合,その操作が完了した後に,もう 1 つのスレッドの出力操作が開始されます。
オペレーティング・システムは,スレッドによる装置またはファイルの参照順序は決定しません。たとえば,マルチスレッド・アプリケーションの非決定論的な性質のために,個々のスレッドがファイルへの書き出しを行うような場合,順番編成ファイル中の記録が,アプリケーションが実行されるたびに異なる順序で書き出される可能性があります。このような場合,直接探査ファイルの方が順番編成ファイルよりも適しているでしょう。直接探査ファイルを使用できないのであれば,ミューテックスを使って,順番編成ファイルの入力または出力の順序を制約します。
QuickWin プログラムの入力手続のブロック関数には,いくつかの制約条件が適用されます。これらの制約条件については,「QuickWin の使用」を参照してください。