程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> 通過異步程序調用(APC)實現的定時功能

通過異步程序調用(APC)實現的定時功能

編輯:關於VC++

定時器是一個在特定時間或者規則間隔被激發的內核對象。結合定時器的異步程序調用可 以允許回調函數在任何定時器被激發的時候執行。本文的例子代碼顯示了如何實現。

使用本定時器時,你需要把常量_WIN32_WINNT定義為0x0400,並且此常量應該在包之前定義 ,以確保聲明合適的定時器原型函數。

通過調用CreateWaitableTimer()可以創建一 個定時器,此函數返回一個指向內核對象的句柄。若定時器已經存在,你可以通過使用 OpenWaitableTimer()獲得一個進程相關的句柄。無論是通過CreateWaitableTimer() 還是通 過OpenWaitableTimer()獲得的句柄,在不需要定 時器時必須釋放,方法是使用函數 CloseHandle()。

定時的時間通過調用SetWaitableTimer()來設置,可以設置為一個 特定的時刻(如December 16, 1999 at 9:45 PM)或者一個相對的時間(如從現在起每五分 鐘)。函數SetWaitableTime()定時的時間參數要求LARGE_INTEGER類型。這個值應該符合在 結構體FILETIME中描述的格式。如果值是正的,代表一個特定的時刻。如果值是負的,代表 以100納秒為單位的相對時間。後面的示例代碼中使用的是相對時間。在調用 SetWaitableTimer()函數後,定時器將在每5秒被激發一次。

你也可以將定時器設置 為周期性的自我激發,方法是向SetWaitableTimer()的第三個參數傳遞一個周期參數(以毫 秒為單位)。在CreateWaitableTimer()的第二個參數傳遞FALSE可以產生一個自動歸零的定 時器。本例設置周期為兩秒的定時器。

當設置了定時器之後,你就可以將APC與其結 合起來。這裡把APC函數稱作完全例程。完全例程的地址作為SetWaitableTimer()的第四個參 數。第五個參數是一個空類型的指針,你可以使用它來傳遞完全例程的參數。

在所有 的APC中,要執行一個完全例程則線程必須處於監聽狀態。完全例程將總是被調用 SetWaitableTimer()的相同的線程執行,所以此線程必須將必須其自身置於監聽狀態。可以 調用下面的任何一個監聽函數來完成監聽狀態的設置:

SleepEx();

WaitForSingleObjectEx();

WaitForMultipleObjectsEx();

MsgWaitForMultipleObjectsEx();

SignalObjectAndWait();

任何一個線程都有一個APC隊列。在調用上面的任何一個函數時,如果線程的APC隊列中有 實體,則此線程不會進入休眠狀態,取而代之要做的是將實體從APC隊列中取出,然後調用相 應的完全例程。

如果在APC隊列中不存在實體,那麼線程將會被掛起,直至等待條件 滿足為止。滿足等待條件的有:一個實體加入到APC隊列中,超時,激活句柄等,以及在調用 MsgWaitForMultipleObjectsEx()情況下,一個消息進入到線程的一個消息隊列中。若等待條 件滿足的是APC隊列中的一個實體,那麼線程會被激活,並且執行完全例程,這種情況下的函 數的返回值是 WAIT_IO_COMPLETION.

【重要提示】

1、在執行完一個完全例程 之後,系統會檢查在APC中剩下的實體以處理。一個監視函數僅僅在處理完所有APC實體後才 返回。因此,如果實體加入到APC隊列的速度比處理的更快的話,則調用這些函數可能永遠也 不能返回。特別當定時等待的時間比起要求執行完全例程的時間更短的話,這種情況更容易 發生。

2、當使用APC來實現定時器時,設置定時的線程不應該等待定時器的句柄。如 果等待定時器的句柄的話,則喚起這個線程的原因是定時器被激活,而不是有實體加入到APC 隊列中。這時線程將不再處於監聽狀態,所以完全例程也不會被調用。在本例中,Sleep()被 用於將線程置於監聽狀態。在定時器激活後,如果有實體被加入到此線程的APC隊列中時, Sleep()就會喚醒此線程。

【示例代碼】

#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <stdio.h>
#define _SECOND 10000000
typedef struct _MYDATA {
  TCHAR *szText;
  DWORD dwValue;
} MYDATA;
VOID CALLBACK TimerAPCProc(
  LPVOID lpArg,        // Data value
  DWORD dwTimerLowValue,   // Timer low value
  DWORD dwTimerHighValue )  // Timer high value
{
  MYDATA *pMyData = (MYDATA *)lpArg;
  printf( "Message: %s\nValue: % d\n\n", pMyData->szText,
     pMyData->dwValue );
  MessageBeep(0);
}
void main( void )
{
  HANDLE      hTimer;
  BOOL      bSuccess;
  __int64     qwDueTime;
  LARGE_INTEGER  liDueTime;
  MYDATA     MyData;
  TCHAR       szError[255];
  MyData.szText = "This is my data.";
  MyData.dwValue = 100;
  if ( hTimer = CreateWaitableTimer(
       NULL,          // Default security attributes
      FALSE,          // Create auto-reset timer
      "MyTimer" ) )      // Name of waitable timer
  {
   __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
     {
      wsprintf( szError, "SetWaitableTimer failed with Error \
        %d.", GetLastError() );
      MessageBox( NULL, szError, "Error", MB_ICONEXCLAMATION );
     }
   }
   __finally
    {
     CloseHandle( hTimer );
   }
  }
  else
  {
   wsprintf( szError, "CreateWaitableTimer failed with Error %d.",
     GetLastError() );
   MessageBox( NULL, szError, "Error", MB_ICONEXCLAMATION );
  }
}

原文參考: Using a Waitable Timer with an Asynchronous Procedure Call

  1. 上一頁:
  2. 下一頁:
欄目導航
Copyright © 程式師世界 All Rights Reserved