Share via


搭配非同步程序呼叫使用可等候計時器

下列範例會將 非同步程序呼叫 關聯 (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;
}

使用可等候計時器物件