通过异步过程调用使用可等待计时器

以下示例将 APC) 函数(也称为完成例程) (异步过程调用 与设置 计时器时的可等待计时器 相关联。 完成例程的地址是 SetWaitableTimer 函数的第四个参数。 第五个参数是 void 指针,可用于将参数传递给完成例程。

完成例程将由调用 SetWaitableTimer 的同一线程执行。 此线程必须处于可警报状态才能执行完成例程。 它通过调用 SleepEx 函数(一个可发出警报的函数)来实现此目的。

每个线程都有一个 APC 队列。 如果在调用某个可警报函数时线程的 APC 队列中有一个条目,则线程不会进入睡眠状态。 相反,将从 APC 队列中删除该条目,并调用完成例程。

如果 APC 队列中不存在任何条目,线程将挂起,直到满足等待。 可以通过将条目添加到 APC 队列、超时或发出信号的句柄来满足等待。 如果 APC 队列中的某个条目满足等待,则会唤醒线程并调用完成例程。 在这种情况下,函数的返回值 WAIT_IO_COMPLETION

执行完成例程后,系统会检查 APC 队列中是否有另一个要处理的条目。 只有在处理完所有 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;
}

使用可等待计时器对象