通过异步程序调用(APC)实现的定时功能

 

  定时器是一个在特定时间或者规则间隔被激发的内核对象。结合定时器的异步程序调用可以允许回调函数在任何定时器被激发的时候执行。本文的例子代码显示了如何实现。
  使用本定时器时,你需要把常量_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

 

 

相信大家都用过VC提供的Timer,是不是觉得很不好用?
当系统资源消耗很厉害的时候,Timer就会停止,因为它的优先级很低!并且它的启动时间
不能任意更改,只能与设定的间隔时间一致,因此使用上就会受到一些限制,精确度也比较
低,一般使用的时间间隔不小于50ms.
看到Timer的一些缺点,我们可以使用Windows提供的CreateWaitableTimer,这个Timer可
以设定多长时间后启动Timer(该时间以100ns为单位,即0.1ms),并且也可以像普通Timer
一样设定间隔时间,最小为ms. 

正文
要包含头的文件:
#include <windows.h>
#include <stdio.h>

如果需要Callback Function,也可以定义:
VOID CALLBACK TimerAPCProc( LPVOID lpArg,               // Data value.
                            DWORD dwTimerLowValue,      // Timer low value.
                            DWORD dwTimerHighValue ) {  // Timer high value.

} //该Function在每次Timer被触发时,被调用

CreateWaitableTimer函数原型:

HANDLE CreateWaitableTimer(
  LPSECURITY_ATTRIBUTES lpTimerAttributes, // SD
  BOOL bManualReset,                       // reset type
  LPCTSTR lpTimerName                      // object name
);

我们要定义一个HANDLE来接受该函数的返回值:
HANDLE hTimer = NULL;
hTimer = CreateWaitableTimer(NULL, TRUE, "MyTimer");

如果创建不成功,hTimer = NULL;否则为Timer Object的Handle.

使用Timer时,需要调用SetWaitableTimer,该函数原型:
BOOL SetWaitableTimer(
  HANDLE hTimer,                          // handle to timer
  const LARGE_INTEGER *pDueTime,          // timer due time
  LONG lPeriod,                           // timer interval
  PTIMERAPCROUTINE pfnCompletionRoutine,  // completion routine
  LPVOID lpArgToCompletionRoutine,        // completion routine parameter
  BOOL fResume                            // resume state
);

使用
SetWaitableTimer(hTimer, &liDueTime, 500, TimerAPCProc, NULL, FALSE))
将Timer间隔设定为500ms, Callback Function为TimerAPCProc
如果成功返回TRUE,否则返回FALSE.

详细使用可以参看MSDN.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值