待機関数

待機関数 を使用すると、スレッドは独自の実行をブロックできます。 待機関数は、指定された条件が満たされるまで戻りません。 wait 関数の種類によって、使用される条件のセットが決まります。 待機関数が呼び出されると、待機条件が満たされているかどうかを確認します。 条件が満たされていない場合、呼び出し元のスレッドは、待機条件の条件が満たされるか、指定されたタイムアウト間隔が経過するまで待機状態になります。

単一オブジェクト待機関数

SignalObjectAndWaitWaitForSingleObjectおよび WaitForSingleObjectEx 関数には、1 つの同期オブジェクトへのハンドルが必要です。 これらの関数は、次のいずれかが発生すると を返します。

  • 指定されたオブジェクトはシグナル状態です。
  • タイムアウト間隔が経過します。 タイムアウト間隔を INFINITE に設定して、待機がタイムアウトしないことを指定できます。

SignalObjectAndWait 関数を使用すると、呼び出し元スレッドはオブジェクトの状態をシグナルにアトミックに設定し、別のオブジェクトの状態がシグナルに設定されるまで待機できます。

複数オブジェクト待機関数

WaitForMultipleObjectsWaitForMultipleObjectsExMsgWaitForMultipleObjectsMsgWaitForMultipleObjectsEx 関数を使用すると、呼び出し元のスレッドで、1 つ以上の同期オブジェクト ハンドルを含む配列を指定できます。 これらの関数は、次のいずれかが発生すると を返します。

  • 指定したオブジェクトの状態が signaled に設定されているか、すべてのオブジェクトの状態が signaled に設定されています。 関数呼び出しで 1 つまたはすべての状態を使用するかどうかを制御します。
  • タイムアウト間隔が経過します。 タイムアウト間隔を INFINITE に設定して、待機がタイムアウトしないことを指定できます。

MsgWaitForMultipleObjects および MsgWaitForMultipleObjectsEx 関数を使用すると、オブジェクト ハンドル配列に入力イベント オブジェクトを指定できます。 これは、スレッドの入力キューで待機する入力の種類を指定するときに行われます。 たとえば、スレッドは MsgWaitForMultipleObjects を 使用して、指定したオブジェクトの状態がシグナル通知に設定され、スレッドの入力キューにマウス入力が使用可能になるまで、その実行をブロックできます。 スレッドは、GetMessage または PeekMessageA または PeekMessageW 関数を使用して入力を取得できます。

すべてのオブジェクトの状態がシグナルに設定されるのを待つ場合、これらの複数オブジェクト関数は、すべてのオブジェクトの状態がシグナル通知に設定されるまで、指定されたオブジェクトの状態を変更しません。 たとえば、ミューテックス オブジェクトの状態は通知できますが、呼び出し元のスレッドは、配列で指定された他のオブジェクトの状態も signaled に設定されるまで所有権を取得しません。 それまでは、他のスレッドがミューテックス オブジェクトの所有権を取得し、その状態を非署名に設定する場合があります。

1 つのオブジェクトの状態がシグナルに設定されるのを待つ場合、これらの複数オブジェクト関数は、オブジェクトの 1 つがシグナルを受け取るまで、インデックス 0 で始まる順序で配列内のハンドルをチェックします。 複数のオブジェクトがシグナルを受け取った場合、この関数は、オブジェクトがシグナルを受け取った配列内の最初のハンドルのインデックスを返します。

アラート可能な待機関数

MsgWaitForMultipleObjectsExSignalObjectAndWaitWaitForMultipleObjectsExおよび WaitForSingleObjectEx 関数は、必要に応じてアラート可能な待機操作を実行できる点で、他の待機関数とは異なります。 警告可能な待機操作では、指定された条件が満たされたときに関数は を返すことができますが、システムが待機中のスレッドによって実行するために I/O 完了ルーチンまたは APC をキューに入れた場合にも、を返すことができます。 アラート可能な待機操作と I/O 完了ルーチンの詳細については、「 同期」および「入力と出力の重複」を参照してください。 APC の詳細については、「 非同期プロシージャ呼び出し」を参照してください。

登録済み待機関数

RegisterWaitForSingleObject 関数は、待機操作がスレッド プールのスレッドによって実行されるという点で、他の待機関数とは異なります。 指定した条件が満たされると、コールバック関数はスレッド プールのワーカー スレッドによって実行されます。

既定では、登録済みの待機操作は複数待機操作です。 UnregisterWaitEx 関数を呼び出して操作を取り消すまで、イベントが通知されるたびにタイマーがリセットされます (またはタイムアウト間隔が経過します)。 待機操作を 1 回だけ実行するように指定するには、RegisterWaitForSingleObjectdwFlags パラメーターをWT_EXECUTEONLYONCEに設定します。

スレッドが APC を使用する関数を呼び出す場合は、RegisterWaitForSingleObjectdwFlags パラメーターを WT_EXECUTEINPERSISTENTTHREAD に設定します。

アドレスの待機中

スレッドは WaitOnAddress 関数を使用して、ターゲット アドレスの値が望ましくない値から他の値に変わるのを待機できます。 これにより、スレッドは、スレッドが望ましくない値をキャプチャしたが、スレッドが待機する前に値が変更されたときに発生する可能性がある同期の問題をスピンまたは処理することなく、値が変更されるのを待機できます。

WaitOnAddress は、ターゲット値を変更するコードが 、WakeByAddressSingle を呼び出して 1 つの待機スレッドをスリープ解除するか 、WakeByAddressAll を呼び出してすべての待機スレッドをスリープ解除することで、変更を通知するとを返します。 WaitOnAddress でタイムアウト間隔を指定し、スレッドがウェイク関数を呼び出さない場合、タイムアウト間隔が経過すると関数は を返します。 タイムアウト間隔が指定されていない場合、スレッドは無期限に待機します。

待機関数とタイムアウト間隔

指定されたタイムアウト間隔の精度は、システム クロックの解像度によって異なります。 システム クロックは、一定のレートで "ティック" します。 タイムアウト間隔がシステム クロックの解像度より小さい場合、待機は指定された時間よりも短い時間でタイムアウトする可能性があります。 タイムアウト間隔が 1 ティックより大きく、2 未満の場合、待機は 1 ティックと 2 ティックの間の任意の場所にできます。

待機関数のタイムアウト間隔の精度を高めるには、 timeGetDevCaps 関数を呼び出して、サポートされている最小タイマー解像度と timeBeginPeriod 関数を調べてタイマーの解像度を最小値に設定します。 timeBeginPeriod を呼び出すときは注意が必要です。頻繁な呼び出しは、システム クロック、システム電源使用量、スケジューラに大きな影響を与える可能性があります。 timeBeginPeriod を呼び出す場合は、アプリケーションの早い段階で 1 回呼び出し、アプリケーションの最後で timeEndPeriod 関数を必ず呼び出してください。

待機関数と同期オブジェクト

待機関数は、一部の種類の 同期オブジェクトの状態を変更できます。 変更は、シグナル状態によって関数が返されたオブジェクトに対してのみ行われます。 待機関数は、同期オブジェクトの状態を次のように変更できます。

  • セマフォ オブジェクトの数は 1 ずつ減少し、セマフォの数が 0 の場合、セマフォの状態は非割り当てに設定されます。
  • ミューテックス、自動リセット イベント、および変更通知オブジェクトの状態は、非署名に設定されます。
  • 同期タイマーの状態は、非署名に設定されます。
  • 手動リセット イベント、手動リセット タイマー、プロセス、スレッド、コンソール入力オブジェクトの状態は、待機関数の影響を受けません。

待機関数と Windows の作成

直接または間接的にウィンドウを作成する待機関数とコードを使用する場合は注意する必要があります。 スレッドがウィンドウを作成する場合は、メッセージを処理する必要があります。 メッセージ ブロードキャストは、システム内のすべてのウィンドウに送信されます。 タイムアウト間隔のない待機関数を使用するスレッドがある場合、システムはデッドロックします。 ウィンドウを間接的に作成するコードの例として、DDE と CoInitialize 関数の 2 つがあります。 したがって、ウィンドウを作成するスレッドがある場合は、他の待機関数ではなく、 MsgWaitForMultipleObjects または MsgWaitForMultipleObjectsEx を使用します。