リソースの共有

各スレッドは独自のスタックと、独自の CPU レジスターのコピーを持っています。ファイル、ユニット、スタティック・データ、およびヒープメモリーなどの他のリソースは、プロセス内のすべてのスレッドで共有されます。これらの共有リソースを使用するスレッドは、互いに作業の調整を行わなければなりません。リソースを同期する方法は複数あります。

これらのオブジェクトの状態には、シグナル状態と非シグナル状態があります。シグナル状態は、リソースがプロセスまたはスレッドから使用できる状態にあることを示します。非シグナル状態は、リソースが使用中であることを示します。

リソース共有メカニズムの作成、初期化、および終了を管理するルーチンを次のセクションで説明します。これらのルーチンの中には、オブジェクトの状態を非シグナル状態からシグナル状態に変更するものがあります。また、WaitForSingleObject ルーチンおよび WaitForMultipleObjects ルーチンもオブジェクトの状態を変更します。これらの関数に関する詳細は、「スレッドの同期」を参照してください。

このセクションでは、以下の説明を行います。

クリティカル・セクション

クリティカル・セクションでスレッドを同期するには、InitializeCriticalSection を呼び出し、スレッドを初期化する必要があります。グローバル変数の処理を開始するときには EnterCriticalSection を呼び出し、アプリケーションが処理を終了したときには LeaveCriticalSection を呼び出します。EnterCriticalSectionLeaveCriticalSection は、どちらもアプリケーション内で何度でも呼び出せます。

Mutex

Mutex オブジェクトは、CreateMutex で作成できます。この関数は、同一の Mutex がすでに存在している場合 (別のプロセスまたはスレッドによって同じ名前の Mutex が作成されていた場合) にエラーを返します。CreateMutex を呼び出した後に GetLastError を呼び出し、エラー状態を示す ERROR_ALREADY_EXISTS が発生しているかどうかを確認してください。また、OpenMutex 関数を使用しても、その名前の Mutex オブジェクトがすでに存在しているかどうかを調べることができます。OpenMutex は、指定された名前の Mutex がすでに存在している場合はそのハンドルを返し、存在しない場合にはヌルを返します。OpenMutex が使用されても、Mutex オブジェクトはシグナル状態に変更されません。オブジェクトの状態をシグナル状態に変更するには、「スレッドの同期」で説明している待機ルーチンを使用します。

ReleaseMutex は、Mutex を非シグナル状態からシグナル状態に変更します。この関数は、呼び出し側のスレッドがその Mutex を所有している場合にのみ効果を持ちます。Mutex がシグナル状態になっていれば、待機している任意のスレッドがその Mutex を獲得して、実行を開始することができます。

セマフォー

セマフォーを処理するための関数は、Mutex を管理する関数とほぼ同じです。CreateSemaphore は、リソースを同時に参照できるスレッド数 (リソースカウント) の初期値と最大値を指定して、セマフォーを作成します。OpenSemaphore は、OpenMutex と同様に、指定されたセマフォー・オブジェクトが存在している場合、そのハンドルを返します。このハンドルは、ハンドルを必要とする任意の関数 (「スレッドの同期」で説明している待機関数など) で使用できます。OpenSemaphore が呼び出されても、リソースカウントが減るわけではありません。このカウントは、リソースが使用可能になるのを待っている関数によって減らされます。

ReleaseSemaphore を使用すると、リソースカウントが指定された数だけ増えます。この関数は、スレッドがリソースの処理を終了したときに呼び出します。他の使用方法として、CreateSemaphore を呼び出して、リソースカウントの初期値をゼロに指定し、初期化プロセス時にリソースが参照されるのを禁止することができます。そして、アプリケーションの初期化が終了した後に、ReleaseSemaphore を呼び出し、リソースカウントを最大値まで増やします。

イベント

イベント・オブジェクトを使用することで、他のスレッドの実行をトリガーできます。イベントは、あるスレッドが別の複数のスレッドにデータを提供している場合に使用できます。イベント・オブジェクトを作成するには、CreateEvent 関数を使用します。作成側のスレッドは、オブジェクトの初期状態と、そのオブジェクトが手動リセットイベントまたは自動リセットイベントのどちらなのかを指定します。手動リセットイベントは、ResetEvent への呼び出しによって明示的にリセットされるまではシグナル状態を保つイベントです。自動リセットイベントは、待機しているスレッドが解放されたときに、自動的にシステムによってリセットされます。

イベント・オブジェクトをシグナル状態に設定するには、SetEvent または PulseEvent を使用します。OpenEvent は、イベントへのハンドルを返します。このハンドルは他の関数呼び出しで使用することができます。ReleaseEvent は、イベントの所有権を解放します。

メモリーの使用とスレッドスタック

各スレッドは独自のスタックを持っているので、使用するスタティック・データをできるだけ少なくすることで、データ項目をめぐって競合が発生する可能性を減らすことができます。プログラムを設計するときには、スレッド固有のすべてのデータに対して、自動スタック変数を使用することを推奨します。マルチスレッド・ルーチン内で宣言されたすべての変数は、デフォルトではスタティックになり、スレッド間で共有されます。他のスレッドで使用されている変数を上書きしないようにするには、次の方法があります。

自動変数として宣言された変数は、スレッドとともに保存されるスレッド・コンテキストの一部であるスタックに置かれます。プロシージャー内の自動変数は、プロシージャーが実行を完了した時点で破棄されます。

I/O 操作

ファイルとユニットはスレッド間で共有されますが、これらの共有リソースの使用をスレッド間で調整しなくても良い場合があります。Fortran では、個々の入力文および出力文がアトミックな操作として扱われます。2 つの異なるスレッドが同じユニットに対して同時に書き込みを試み、1 つのスレッドの出力操作が開始された場合、必ずその操作が完了した後に、もう 1 つのスレッドの出力操作が開始されます。

オペレーティング・システムは、スレッドによるユニットまたはファイルへの参照順序は決定しません。例えば、マルチスレッド・アプリケーションでは、その特有の非決定的な性質のために、複数のスレッドが同一のシーケンシャル・ファイルへ書き込みを行う場合、アプリケーションが実行されるたびに、ファイル内のレコードが異なる順序で書き込まれる可能性があります。このような場合、直接アクセスを行えるファイルの方がシーケンシャル・ファイルよりも適していることがあります。直接アクセスを行えるファイルが使用できない場合は、Mutex を使用して、シーケンシャル・ファイルの入力または出力の順序を制約できます。

QuickWin プログラムの入力プロシージャーのブロック関数には、いくつかの制約条件が適用されます。これらの制約条件に関する詳細は、「アプリケーションのビルド」の「QuickWin の使用」を参照してください。