プロセスにスレッドを追加するときには、プロセスに掛かる負担を考慮する必要があります。作成するスレッド数は、アプリケーションの応答と性能が向上する範囲にとどめるようにしてください。マルチタスク化すると時間を節約できますが、複数のスレッドを追跡するために、追加の CPU 時間が必要になることに注意してください。作成するスレッド数を決定するときには、どのデータがプロセス特有のデータであり、どのデータがスレッド特有のデータであるのかという点も配慮する必要があります。「リソースの共有」セクションでは、変数とデータへの参照を同期する方法について説明しています。
CreateThread 関数を呼び出すことで、スレッドを作成し、セキュリティー属性とメモリースタックのサイズを指定でき、また、スレッドが実行するルーチン名も指定できます。Windows* は、スレッドを含むアプリケーションの仮想アドレス空間内に、メモリーをスレッドスタック用に割り当てます。スレッドの処理が終了した場合、CloseHandle ルーチンでそのスレッドが使用していたリソースを解放できます。
CreateThread 関数は、新しいスレッドを作成します。その戻り値は、INTEGER(4) のスレッドハンドルで、スレッドとの通信時およびスレッドを閉じるときに使用されます。次にこの関数の構文を示します。
CreateThread (security, stack, thread_func, argument, flags, thread_id)
CreateThread が実行するルーチン名を示す thread_func 以外のすべての引数は、INTEGER(4) 変数です。次に引数の詳細を示します。
引数 |
説明 |
---|---|
security |
この引数には、IFMT.F90 で定義された SECURITY_ATTRIBUTES 型が使用されます。security にゼロを指定すると、スレッドは親プロセスから引き継いだデフォルトのセキュリティー属性を持ちます。プロセスとスレッドのセキュリティー属性の設定についての詳細は、オンライン・リファレンスの『Platform SDK』(英語) を参照してください。 |
stack |
新規作成するスレッドのスタックサイズを定義します。アプリケーションのすべてのデフォルトスタック空間は、最初の実行スレッドに割り当てられます。そのため、プログラムが追加するスレッドごとに、別のスタック用にメモリーをどのくらい割り当てるかを指定する必要があります。CreateThread を呼び出すことで、作成する各スレッドのスタックサイズを指定することができます。値にゼロを指定する場合、スタックはアプリケーションのプライマリー・スレッドと同じサイズを持ちます。スタックのサイズは、必要に応じて最大 1MB まで動的に増やすことができます。 |
thread_func |
スレッド関数の開始アドレスです。 |
argument |
省略可能。thread_func に渡す引数です。このパラメーターとその使用方法は、プログラム内で定義します。 |
flags |
この引数を使用することで、信号が送信されるまで処理を開始しないスレッドを作成できます。flags 引数は、ゼロまたは CREATE_SUSPENDED のどちらかの値になります。ゼロを指定すると、スレッドは作成された直後に実行を開始します。CREATE_SUSPENDED を指定すると、スレッドは作成されますが、ResumeThread 関数が呼び出されるまで実行されません。 |
thread_id |
この引数は、CreateThread によって返されます。これはスレッド固有の識別子で、他のマルチスレッド・ルーチンを呼び出す際に使用できます。このスレッドが実行している間は、他のスレッドは同じ識別子を持つことはできません。ただし、このスレッドが終了したら、オペレーティング・システムによって他のスレッドのためにこの識別子が再び使用される場合があります。 スレッドは、スレッド固有の識別子だけでなく、ハンドルを使用しても参照できます。WaitForSingleObject や WaitForMultipleObjects などの同期関数は、スレッドハンドルを引数として使用します。 |
ExitThread ルーチンを使用することで、スレッドの実行を停止することができます。構文は次のとおりです: CALL EXITTHREAD ( [ Termination Status ] )
終了状態 (Termination Status) は、他のスレッドから問い合わせることができます。終了状態がゼロの場合、正常終了を示します。プログラムで、これ以外の終了状態の値とその意味を割り当てることができます。
呼び出し側のスレッドは、呼び出したスレッドが不要になると、そのスレッドのハンドルを閉じる必要があります。スレッドが使用しているメモリーを解放するには、CloseHandle ルーチンを使用します。スレッド・オブジェクトは、最後のスレッドハンドルが閉じられるまで削除されません。
1 つのスレッドに対して複数のハンドルが開かれることがあります。例えば、プログラムが 2 つのスレッドを作成し、一方のスレッドがもう一方のスレッドからの情報を待っている場合を考えます。この例では、最初のスレッドに対して、情報を要求しているスレッドからと、そのスレッドを作成したスレッドからの、合わせて 2 つのハンドルが開かれています。外側のプロセスが終了すると、すべてのハンドルは暗黙的に閉じられます。
TerminateThread ルーチンを使用することにより、スレッドは別のスレッドを終了させることができます (両方のスレッドのセキュリティー属性が適切に設定されている場合)。スレッドに添付されている DLL には、スレッドの終了が通知されず、その初期スタック用に割り当てられたメモリーは解放されません。TerminateThread は、緊急の場合にのみ使用するようにしてください。
スレッドの優先度のスケジューリングは、GetThreadPriority 関数と SetThreadPriority 関数によってサポートされています。スレッドの優先度クラスを使用して、実行時間が重要なアプリケーションと、スケジューリングの要件が通常レベル、またはそれ以下であるアプリケーションを区別することができます。優先度を操作する必要がある場合、スレッドにあまりに高い優先度を割り当てると、使用可能な CPU 時間をすべて消費してしまう可能性があるので注意してください。基本優先度が 11 よりも高いスレッドは、オペレーティング・システムの通常動作に影響する可能性があります。REALTIME_PRIORITY_CLASS を使用すると、ディスクキャッシュがフラッシュしない、マウスがハングするといった状況が生じることがあります。
スレッドは、他のスレッドと通信を行う際に、自分自身を参照するために疑似ハンドルを使用します。疑似ハンドルとは、現在のスレッドハンドルとして解釈される特殊な定数です。疑似ハンドルは呼び出し側のスレッドでのみ有効で、他のスレッドが継承することはできません。GetCurrentThread 関数は、現在のスレッドの疑似ハンドルを返します。呼び出し側のスレッドは、このハンドルを使用して、スレッドハンドルが必要な場面で自分自身を指定することができます。疑似ハンドルは継承できません。
スレッドの識別子を取得するには、GetCurrentThreadId 関数を使用します。識別子は、システム内のスレッドを、それが終了するまで一意に識別します。識別子を使用することで、識別子が必要な場面でそのスレッドを指定することができます。
GetExitCodeThread を使用することで、スレッドがアクティブなのかどうかを知ることができ、またアクティブでない場合には、その終了状態を知ることができます。終了状態の詳しい情報を取得するには、GetLastError を呼び出します。あるルーチンが、異なるスレッドによって実行されているタスクに依存する場合、GetExitCodeThread ではなく、「スレッドの同期」で説明している待機関数を使用してください。