Использование таймеров с возможностью ожидания с асинхронным вызовом процедуры
В следующем примере функция асинхронного вызова процедуры (APC), также известная как подпрограмма завершения, связывается с таймером ожидания при установке таймера. Адрес подпрограммы завершения — это четвертый параметр функции SetWaitableTimer . Пятый параметр — это указатель void, который можно использовать для передачи аргументов в подпрограмму завершения.
Подпрограмма завершения будет выполняться тем же потоком, который называется SetWaitableTimer. Чтобы выполнить подпрограмму завершения, этот поток должен находиться в состоянии предупреждения. Для этого вызывается функция SleepEx , которая является функцией с оповещениями.
Каждый поток имеет очередь APC. Если в очереди APC потока есть запись во время вызова одной из оповещенных функций, поток не переводится в спящий режим. Вместо этого запись удаляется из очереди APC и вызывается процедура завершения.
Если в очереди APC нет записи, поток приостанавливается до тех пор, пока не будет выполнено ожидание. Ожидание можно выполнить путем добавления записи в очередь APC, времени ожидания или передачи сигнала дескриптора. Если ожидание удовлетворяется записью в очереди APC, поток пробуждается и вызывается процедура завершения. В этом случае возвращаемое значение функции равно WAIT_IO_COMPLETION.
После выполнения процедуры завершения система проверяет наличие другой записи в очереди APC для обработки. Оповещаемая функция будет возвращать только после обработки всех записей APC. Таким образом, если записи добавляются в очередь APC быстрее, чем они могут быть обработаны, возможно, вызов оповещенной функции никогда не вернется. Это особенно возможно с таймерами ожидания, если период меньше времени, необходимого для выполнения процедуры завершения.
При использовании таймера ожидания с APC поток, который задает таймер, не должен ждать дескриптора таймера. Это приведет к пробуждению потока в результате передачи сигнала таймера, а не в результате добавления записи в очередь APC. В результате поток больше не находится в состоянии предупреждения и подпрограмма завершения не вызывается. В следующем коде вызов SleepEx пробуждает поток при добавлении записи в очередь APC потока после установки таймера в состояние сигнала.
#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;
}
Связанные темы