几何尺寸与公差论坛

 找回密码
 注册
查看: 1953|回复: 0

How To Use a Waitable Timer with an Asynchronous Procedure Call

[复制链接]
发表于 2010-4-24 22:32:31 | 显示全部楼层 |阅读模式
When using waitable timers, you need to define the constant _WIN32_WINNT as  0x0400. This constant should be defined before the inclusion of  <windows.h> to ensure that the proper waitable timer function prototypes  are declared.

You create a waitable timer by calling  CreateWaitableTimer(). This function returns a handle to the kernel object. If  the timer already exists, you can obtain a process-relative handle by using  OpenWaitableTimer(). The handle, whether obtained through CreateWaitableTimer()  or OpenWaitableTimer(), must be released when the timer is no longer needed. Do  this by calling CloseHandle().

The timer is set with a call to  SetWaitableTimer(). The timer can be set for a specific time (for example,  December 16, 1999 at 9:45 PM) or a relative time (for example, 5 minutes from  now). SetWaitableTimer() requires a LARGE_INTEGER for the due time. This value  should be in the format described by the FILETIME structure. If the value is  positive, it represents a specific time. If it is negative, it represents a  relative time in 100-nanosecond units. The sample code shown later uses a  relative time. The timer will become signaled 5 seconds after the call to  SetWaitableTimer().

You can also set the timer to periodically re-signal  itself. Do this by passing a periodic value (in milliseconds) as the third  parameter to SetWaitableTimer(). If a periodic timer is required, you should  create the timer as an auto-reset timer by passing FALSE as the second parameter  to CreateWaitableTimer(). This sample sets a timer with a period of 2 seconds.  

You can associate an asynchronous procedure call (APC) function with a  waitable timer when the timer is set. The APC function is called a completion  routine. The address of the completion routine is the fourth parameter to  SetWaitableTimer(). The fifth parameter is a void pointer that you can use to  pass arguments to the completion routine.

As with all APCs, a thread  must be in an alertable state to execute the completion routine. The completion  routine will always be executed by the same thread that called  SetWaitableTimer(). Thus, this thread must eventually put itself in an alertable  state. It accomplishes this by calling one of the following alertable functions:   SleepEx()
WaitForSingleObjectEx()
WaitForMultipleObjectsEx()
MsgWaitForMultipleObjectsEx()
SignalObjectAndWait()

Each  thread has an APC queue. If there is an entry in the thread's APC queue at the  time that one of the above functions is called, the thread is not put to sleep.  Instead, the entry is removed from the APC queue and the completion routine is  called.

If no entry exists in the APC queue, the thread is suspended  until the wait is satisfied. The wait can be satisfied by adding an entry to the  APC queue, by a timeout, by a handle becoming signaled, or, in the case of  MsgWaitForMultipleObjectsEx(), by adding a message to one of the thread's  message queues. If the wait is satisfied by an entry in the APC queue, the  thread is awakened and the completion routine is called. In this case, the  return value of the function is WAIT_IO_COMPLETION.

IMPORTANT NOTE:  After the completion routine is executed, the system checks for another entry in  the APC queue to process. An alertable function will return only after all APC  entries have been processed. It is, therefore, possible that a call to one of  these functions will never return if entries are being added to the APC queue  faster than they can be processed. This is especially possible with waitable  timers, where the period is shorter than the amount of time required to execute  the completion routine.

When you are using a waitable timer with an APC,  the thread that sets the timer SHOULD NOT wait on the handle of the timer. By  doing this, you cause the thread to wake up as a result of the timer becoming  signaled rather than as the result of an entry being added to the APC queue. As  a result, the thread is no longer in an alertable state and the completion  routine is not called. In the following sample, SleepEx() is used to put the  thread in an alertable state. SleepEx() awakens the thread when an entry is  added to the thread's APC queue after the timer becomes signaled.  Back to the  top
Sample Code

                  loadTOCNode(2, 'moreinformation');                   #define _WIN32_WINNT 0x0400
   #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 a negative 64-bit 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 );             // IMPORTANT!!! The thread must be in an
                                   // alertable state to process the APC.
           }

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

   }
       
http://support.microsoft.com/kb/q184796/
您需要登录后才可以回帖 登录 | 注册

本版积分规则

QQ|Archiver|小黑屋|几何尺寸与公差论坛

GMT+8, 2024-12-22 11:44 , Processed in 0.037040 second(s), 19 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表