Uso de temporizadores de espera con una llamada de procedimiento asincrónico

En el ejemplo siguiente se asocia una función asincrónica de llamada a procedimiento (APC), también conocida como rutina de finalización, con un temporizador que se puede esperar cuando se establece el temporizador. La dirección de la rutina de finalización es el cuarto parámetro de la función SetWaitableTimer . El quinto parámetro es un puntero void que se puede usar para pasar argumentos a la rutina de finalización.

La rutina de finalización se ejecutará mediante el mismo subproceso que llamó a SetWaitableTimer. Este subproceso debe estar en un estado de alerta para ejecutar la rutina de finalización. Para ello, llama a la función SleepEx , que es una función que admite alertas.

Cada subproceso tiene una cola de APC. Si hay una entrada en la cola de APC del subproceso en el momento en que se llama a una de las funciones alertables, el subproceso no se pone en suspensión. En su lugar, la entrada se quita de la cola de APC y se llama a la rutina de finalización.

Si no existe ninguna entrada en la cola de APC, el subproceso se suspende hasta que se cumpla la espera. La espera se puede satisfacer agregando una entrada a la cola de APC, un tiempo de espera o un identificador que se señala. Si una entrada de la cola de APC satisface la espera, se activa el subproceso y se llama a la rutina de finalización. En este caso, el valor devuelto de la función es WAIT_IO_COMPLETION.

Una vez ejecutada la rutina de finalización, el sistema comprueba si hay otra entrada en la cola de APC que se va a procesar. Una función alertable solo se devolverá después de que se hayan procesado todas las entradas de APC. Por lo tanto, si las entradas se agregan a la cola de APC más rápido de lo que se pueden procesar, es posible que nunca se devuelva una llamada a una función alertable. Esto es especialmente posible con temporizadores de espera, si el período es menor que la cantidad de tiempo necesario para ejecutar la rutina de finalización.

Cuando se usa un temporizador que se puede esperar con un APC, el subproceso que establece el temporizador no debe esperar en el identificador del temporizador. Al hacerlo, haría que el subproceso se reactivara como resultado de que el temporizador se señale en lugar de como resultado de que se agregue una entrada a la cola de APC. Como resultado, el subproceso ya no está en estado de alerta y no se llama a la rutina de finalización. En el código siguiente, la llamada a SleepEx activa el subproceso cuando se agrega una entrada a la cola de APC del subproceso después de establecer el temporizador en el estado señalado.

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

Uso de objetos de temporizador de espera