非同期プロシージャ 呼び出しでの待機可能タイマーの使用

次の例では、 非同期プロシージャ コール (APC) 関数 (完了ルーチンとも呼ばれます) を、タイマーが設定されたときに 待機可能なタイマー に関連付けます。 完了ルーチンのアドレスは 、SetWaitableTimer 関数の 4 番目のパラメーターです。 5 番目のパラメーターは、完了ルーチンに引数を渡すために使用できる void ポインターです。

完了ルーチンは、 SetWaitableTimer を呼び出したのと同じスレッドによって実行されます。 完了ルーチンを実行するには、このスレッドが警告可能な状態である必要があります。 これを実現するには、アラート可能な関数である SleepEx 関数を呼び出します。

各スレッドには APC キューがあります。 警告可能な関数の 1 つが呼び出された時点でスレッドの APC キューにエントリがある場合、スレッドはスリープ状態になりません。 代わりに、エントリが APC キューから削除され、完了ルーチンが呼び出されます。

APC キューにエントリが存在しない場合、待機が満たされるまでスレッドは中断されます。 APC キューにエントリを追加するか、タイムアウトによって、またはハンドルがシグナル化することで、待機を満たすことができます。 APC キュー内のエントリによって待機が満たされると、スレッドが起動し、完了ルーチンが呼び出されます。 この場合、関数の戻り値は WAIT_IO_COMPLETION

完了ルーチンが実行されると、処理する APC キュー内の別の項目がチェックされます。 alertable 関数は、すべての APC エントリが処理された後にのみを返します。 したがって、エントリが処理できるよりも速く APC キューに追加されている場合は、アラート可能な関数の呼び出しが返されない可能性があります。 これは、完了ルーチンの実行に必要な時間よりも期間が短い場合に、待機可能タイマーで特に可能です。

APC で待機可能タイマーを使用している場合、タイマーを設定するスレッドは、タイマーのハンドルで待機しないようにする必要があります。 これにより、APC キューにエントリが追加された結果ではなく、タイマーがシグナル通知された結果としてスレッドがウェイクアップします。 その結果、スレッドはアラート可能な状態でなくなり、完了ルーチンは呼び出されません。 次のコードでは、タイマーがシグナル状態に設定された後にスレッドの APC キューにエントリが追加されると、 SleepEx の呼び出しによってスレッドが起動されます。

#define UNICODE 1
#define _UNICODE 1

#include <windows.h>
#include <stdio.h>
#include <tchar.h>

#define _SECOND 10000000

typedef struct _MYDATA {
   LPCTSTR szText;
   DWORD dwValue;
} MYDATA;

VOID CALLBACK TimerAPCProc(
   LPVOID lpArg,               // Data value
   DWORD dwTimerLowValue,      // Timer low value
   DWORD dwTimerHighValue )    // Timer high value

{
   // Formal parameters not used in this example.
   UNREFERENCED_PARAMETER(dwTimerLowValue);
   UNREFERENCED_PARAMETER(dwTimerHighValue);

   MYDATA *pMyData = (MYDATA *)lpArg;

   _tprintf( TEXT("Message: %s\nValue: %d\n\n"), pMyData->szText,
          pMyData->dwValue );
   MessageBeep(0);

}

int main( void ) 
{
   HANDLE          hTimer;
   BOOL            bSuccess;
   __int64         qwDueTime;
   LARGE_INTEGER   liDueTime;
   MYDATA          MyData;

   MyData.szText = TEXT("This is my data");
   MyData.dwValue = 100;

   hTimer = CreateWaitableTimer(
           NULL,                   // Default security attributes
           FALSE,                  // Create auto-reset timer
           TEXT("MyTimer"));       // Name of waitable timer
   if (hTimer != NULL)
   {
      __try 
      {
         // Create an integer that will be used to signal the timer 
         // 5 seconds from now.
         qwDueTime = -5 * _SECOND;

         // Copy the relative time into a LARGE_INTEGER.
         liDueTime.LowPart  = (DWORD) ( qwDueTime & 0xFFFFFFFF );
         liDueTime.HighPart = (LONG)  ( qwDueTime >> 32 );

         bSuccess = SetWaitableTimer(
            hTimer,           // Handle to the timer object
            &liDueTime,       // When timer will become signaled
            2000,             // Periodic timer interval of 2 seconds
            TimerAPCProc,     // Completion routine
            &MyData,          // Argument to the completion routine
            FALSE );          // Do not restore a suspended system

         if ( bSuccess ) 
         {
            for ( ; MyData.dwValue < 1000; MyData.dwValue += 100 ) 
            {
               SleepEx(
                  INFINITE,     // Wait forever
                  TRUE );       // Put thread in an alertable state
            }

         } 
         else 
         {
            printf("SetWaitableTimer failed with error %d\n", GetLastError());
         }

      } 
      __finally 
      {
         CloseHandle( hTimer );
      }
   } 
   else 
   {
      printf("CreateWaitableTimer failed with error %d\n", GetLastError());
   }

   return 0;
}

待機可能タイマー オブジェクトの使用