共用方式為


將可等候定時器與非同步程序呼叫一起使用

下列範例會將 異步過程調用 (APC) 函式,也稱為完成例程,與設定定時器時 可等候的定時器 產生關聯。 完成例程的位址是 setWaitableTimer函式的第四個參數。 第五個參數是 void 指標,可用來將自變數傳遞至完成例程。

完成例程將由呼叫 SetWaitableTimer的相同線程執行。 此線程必須處於可警示狀態,才能執行完成例程。 它會藉由呼叫 SleepEx 函式來完成這項作業,這是可提醒的函式。

每個線程都有 APC 佇列。 如果在線程的 APC 佇列中有一個項目,當呼叫其中一個可警示的函式時,線程就不會進入睡眠狀態。 相反地,項目會從 APC 佇列中移除,並呼叫完成例程。

如果 APC 佇列中沒有任何條目存在,執行緒會暫停,直到等待條件滿足為止。 將項目新增至 APC 佇列、逾時或當句柄變為已訊號狀態,即可滿足等候。 如果 APC 佇列中的項目滿足等候,則會喚醒線程,並呼叫完成例程。 在這種情況下,函式的傳回值會是WAIT_IO_COMPLETION

執行完成例程後,系統會檢查 APC 佇列中的另一個待處理的條目。 只有在處理所有 APC 項目之後,才會傳回可警示的函式。 因此,如果條目新增至 APC 佇列的速度比其可處理的速度更快,則對可警示的函數的呼叫可能永遠不會傳回。 如果期間比執行完成例程所需的時間量短,則這特別適用於等候定時器。

當您搭配 APC 使用可等候的定時器時,設定定時器的線程不應該在定時器的句柄上等候。 如此一來,您就會讓線程因為定時器收到訊號而喚醒,而不是將專案新增至 APC 佇列的結果。 因此,線程不再處於可警示狀態,而且不會呼叫完成例程。 在下列程式代碼中,當計時器被設定為訊號狀態後,呼叫 SleepEx 會在專案被新增至該線程的 APC 佇列時,將該線程喚醒。

#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;
}

使用可等候定時器物件