スレッドの起動と停止

プロセスにスレッドを追加するときには,プロセスにとってのコストを考慮する必要があります。作成するスレッド数は,アプリケーションの応答と性能が向上する範囲にとどめるようにしてください。マルチタスク化すると時間の節約ができますが,複数のスレッドを追跡するために,余分な CPU 時間が必要になることに注意してください。作成するスレッド数を決めるときには,どのデータがプロセス特有のデータであり,どのデータがスレッド特有のデータであるのかという点にも配慮する必要があります。変数とデータ参照の同期については,「リソースの共有」を参照してください。

CreateThread 関数を 1 回呼び出すことで,スレッドが作成され,セキュリティ属性とメモリー・スタックの大きさが指定され,スレッドで実行するルーチンの名前が指定されます。Windows は,スレッドを含んでいるアプリケーションの仮想アドレス空間中に,スレッド・スタックのためのメモリーを割り付けます。スレッドが処理を終えると,CloseHandle ルーチンが,そのスレッドが使用していたリソースを解放します。

この節では以下のことを説明しています。

スレッドの起動

CreateThread 関数は,新しいスレッドを作成します。その返し値は INTEGER(4) スレッド・ハンドルで,スレッドとの通信と,スレッドを閉じる際に使用されます。次にこの関数の構文を示します。

CreateThread (security, stack, thread_func, argument, flags, thread_id)

すべての引数は,実行するために CreateThread に対してルーチンの名前を付ける thread_func を除いて,INTEGER(4) の変数です。thread_func に対する最小限の必要事項は,「スレッド・ルーチンの書式」で説明されています。引数は,以下のとおりです。

security DFMT.F90 に定義されている SECURITY_ATTRIBUTES 型です。security がゼロならば,スレッドは親プロセスの基本セキュリティ属性を持ちます。プロセスとスレッドのセキュリティ属性の設定についての詳細は,オンライン・ドキュメントの『Platform SDK』を参照してください。
stack 新しいスレッドのスタックの大きさを定義します。最初の実行スレッドには,アプリケーションの基本スタック空間すべてが割り付けられます。そのため,プログラムが必要とする追加のスレッドごとに,新たなスタックのためにどれだけのメモリーを割り付けるかを指定する必要があります。CreateThread 呼び出しでは,作成する各スレッドのスタックの大きさを指定することができます。値ゼロは,スタックがアプリケーションの主スレッドと同じ大きさであることを示します。スタックの大きさは,必要ならば 1 MB まで動的に増やすことができます。
thread_func スレッド関数の開始アドレスです。
argument thread_func の省略可能な引数です。プログラムは,このパラメタとその使用方法を定義します。
flags スレッドを作成し,それがいつ処理を開始するかを決定します。0 と CREATE_SUSPENDED のどちらかの値を取ります。0 を指定すると,スレッドが作成され,作成直後に実行を開始します。CREATE_SUSPENDED を指定すると,スレッドは作成されますが,ResumeThread 関数を呼び出すまで実行されません。
thread_id CreateThread によって返されます。これはスレッドの一意の識別子で,他のマルチスレッド・ルーチンを呼び出すときに使用できます。このスレッドの実行中は,同じ識別子を持つスレッドは他には存在しません。ただし,このスレッドが完了したら,オペレーティング・システムは他のスレッドのためにこの識別子を再び使用する可能性があります。

スレッドは,一意のスレッド識別子だけでなく,ハンドルでも引用することができます。WaitForSingleObjectWaitForMultipleObjects などの同期関数は,引数としてスレッド・ハンドルを取ります。

スレッドの停止

ExitThread ルーチンを使うと,スレッドは自らの実行を停止することができます。次に構文を示します。

CALL EXITTHREAD ( [ Termination Status] )

終了状態は,他のスレッドから問い合わせることができます。終了状態が 0 の場合,通常の終了を示します。プログラム中では,これ以外の終了状態の値とその意味を割り当てることができます。

呼び出されたスレッドが不要になると,呼び出し側のスレッドはそのスレッドのハンドルを閉じる必要があります。スレッドが使用しているメモリーを解放するには CloseHandle ルーチンを使用します。スレッド・オブジェクトは,最後のスレッド・ハンドルが閉じられるまで削除されません。

1 つのスレッドに対して複数のハンドルが開かれることがあります。たとえば,プログラムが 2 つのスレッドを作成し,一方がもう一方からの情報を待っている場合を考えます。この例では,最初のスレッドに対して,情報を要求しているスレッドからと,そのスレッドを作成したスレッドから,合わせて 2 つのハンドルが開かれています。外側のプロセスが終了すると,すべてのハンドルは暗黙のうちに閉じられます。

TerminateThread ルーチンにより,両方のスレッドのセキュリティ属性が適切に設定されていれば,1 つのスレッドが別のスレッドを終了させることができます。スレッドに添付されている DLL には,スレッドが終了することは通知されず,その初期スタックは割り当てを解除されません。TerminateThread は,緊急の場合にのみ使用するようにしてください。

その他のスレッドをサポートする関数

スレッドの優先度のスケジューリングは,関数 GetThreadPrioritySetThreadPriority を通してサポートされます。スレッドの優先度クラスを使って,実行時間が重要なアプリケーションと,スケジューリングの要件が通常レベル,またはそれ以下であるアプリケーションを区別することができます。優先度を操作する必要がある場合,スレッドにあまりに高い優先度を割り当てると,使用可能な CPU 時間をすべて消費してしまう可能性があるので注意してください。基本優先度が 11 よりも高いスレッドは,オペレーティング・システムの通常の動作に干渉する可能性があります。REALTIME_PRIORITY_CLASS を使用すると,ディスク・キャッシュがフラッシュしない,マウスがハングするといった状況が生じることがあります。

他のスレッドと通信を行うとき,スレッドは自分自身を引用するために疑似ハンドルを使用します。疑似ハンドルは,現在のスレッド・ハンドルとして解釈される特殊な定数です。疑似ハンドルは呼び出し側のスレッドでのみ有効で,他のスレッドが継承することはできません。GetCurrentThread 関数は,現在のスレッドの疑似ハンドルを返します。呼び出し側のスレッドは,このハンドルを使って,スレッド・ハンドルが必要な場面で自分自身を指定することができます。疑似ハンドルは継承されません。

スレッドの識別子を取得するには,GetCurrentThreadId 関数を使用します。識別子は,システム内のスレッドを,それが終了するまで一意に識別します。識別子を使うと,識別子が必要な場面でそのスレッドを指定することができます。

GetExitCodeThread を使うと,スレッドが活動状態なのかどうか,また活動状態でない場合にはその終了状態を知ることができます。終了状態の詳しい情報を知るには,GetLastError を呼び出します。あるルーチンが別のスレッドによって実行される作業に依存している場合,GetExitCodeThread ではなく,「スレッドの同期」で説明している待機関数を使用してください。