Windows NT поддерживает таймеры ожидания (waitable timers), являющихся одним из типов объектов ядра, осуществляющих ожидание.
Вы всегда можете создать собственный сигнал синхронизации, создав синхронизирующий поток, который устанавливает событие в результате пробуждения после вызова функции Sleep. В программе serverNP (программа 11.3) сервер также использует синхронизирующий поток для периодической широковещательной рассылки имени своего канала. Поэтому таймеры ожидания обеспечивают хотя и несколько избыточный, но удобный способ организации выполнения задач на периодической основе или в соответствии с определенным расписанием. В частности, таймер ожидания можно настроить таким образом, чтобы сигнал был сгенерирован в строго определенное время.
Таймер ожидания может быть либо синхронизирующим (synchronization timer), либо сбрасываемым вручную уведомляющим (manual-reset notification timer) таймером. Синхронизирующий таймер связывается с функцией косвенного вызова, аналогичной процедуре завершения расширенного ввода/вывода, тогда как для синхронизации по сбрасываемому вручную уведомляющему таймеру используется функция ожидания.
Для начала потребуется создать дескриптор таймера, используя для этого функцию CreateWaitableTimer.
HANDLE CreateWaitableTimer(LPSECURITY_ATTRIBUTES lpTimerAttributes, BOOL bManualReset, LPCTSTR lpTimerName);
Второй параметр, bManualReset, определяет, таймер какого типа должен быть создан — синхронизирующий или уведомляющий. В программе 14.3 используется синхронизирующий таймер, но, изменив комментарии и настройку параметра, вы легко превратите его в уведомляющий таймер. Заметьте, что существует также функция OpenWaitableTimer, которая может использовать необязательное имя, предоставляемое третьим аргументом.
Первоначально таймер создается в неактивном состоянии, но с помощью функции SetWaitableTimer его можно активизировать и указать начальную временную задержку, а также длительность промежутка времени между периодически вырабатываемыми сигналами.
BOOL SetWaitableTimer(HANDLE hTimer, const LARGE_INTEGER *pDueTime, LONG IPeriod, PTIMERAPCROUTINE pfnCompletionRoutine, LPVOID lpArgToCompletionRoutine, BOOL fResume);
hTimer — действительный дескриптор таймера, созданного с использованием функции CreateWaitableTimer.
Второй параметр, на который указывает указатель pDueTime, может принимать либо положительные значения, соответствующие абсолютному времени, либо отрицательные, соответствующие относительному времени, причем фактические значения выражаются в единицах времени длительностью 100 наносекунд, а их формат описывается структурой FILETIME. Переменные типа FILETIME были введены в главе 3 и уже использовались нами в главе 6 в программе timep (программа 6.2).
Величина интервала между сигналами, указываемая в третьем параметре, выражается в миллисекундах. Если это значение установлено равным 0, то таймер переводится в сигнальное состояние только один раз. При положительных значениях этого параметра таймер является периодическим и срабатывает периодически до тех пор, пока его действие не будет прекращено вызовом функции CancelWaitableTimer. Отрицательные значения указанного интервала не допускаются.
Четвертый параметр, pfnCompletionRoutine, применяется в случае синхронизирующего таймера и указывает адрес процедуры завершения, которая вызывается при переходе таймера в сигнальное состояние и при условии, что поток переходит в состояние дежурного ожидания. При вызове этой процедуры в качестве одного из аргументов используется указатель, определяемый пятым параметром, plArgToComplretionRoutine.
Установив синхронизирующий таймер, вы можете перевести поток в состояние дежурного ожидания путем вызова функции SleepEx, чтобы обеспечить возможность вызова процедуры завершения. В случае сбрасываемого вручную уведомляющего таймера следует организовать ожидание перехода дескриптора таймера в сигнальное состояние. Дескриптор будет оставаться в сигнальном состоянии до следующего вызова функции SetWaitableTimer. Полная версия программы 14.3, находящаяся на Web-сайте, предоставляет вам возможность проводить собственные эксперименты, используя таймер выбранного типа в сочетании с процедурой завершения или ожиданием перехода дескриптора таймера в сигнальное состояние, что в итоге дает четыре различные комбинации.
Последний параметр, fResume, связан с режимами энергосбережения. Для получения более подробной информации по этому вопросу обратитесь к справочной документации.
Функция CancelWaitableTimer используется для отмены действия вызванной перед этим функции SetWaitableTimer, но при этом не изменяет сигнальное состояние таймера. Чтобы это сделать, необходимо в очередной раз вызвать функцию SetWaitableTimer.
Пример: использование таймера ожидания
В программе 14.3 демонстрируется применение таймера ожидания для генерации периодических сигналов.
Программа 14.3. TimeBeep: генерация периодических сигналов
/* Глава 14. TimeBeep.с. Периодическое звуковое оповещение. */
/* Использование: TimeBeep период (в миллисекундах). */
#include "EvryThng.h"
static BOOL WINAPI Handler(DWORD CntrlEvent);
static VOID APIENTRY Beeper(LPVOID, DWORD, DWORD);
volatile static BOOL Exit = FALSE;
HANDLE hTimer;
int _tmain(int argc, LPTSTR argv[]) {
DWORD Count = 0, Period;
LARGE_INTEGER DueTime;
/* Перехват нажатия комбинации клавиш <Ctrl-c> для прекращения операции. См. главу 4. */
SetConsoleCtrlHandler(Handler, TRUE);
Period = _ttoi(argv[1]) * 1000;
DueTime.QuadPart = –(LONGLONG)Period * 10000;
/* Параметр DueTime отрицателен для первого периода ожидания и задается относительно текущего времени. Период ожидания измеряется в мс (10-3 с), a DueTime — в единицах по 100 нc (10-7 с) для согласования с типом FILETIME. */
hTimer = CreateWaitableTimer(NULL, FALSE /* "Таймер синхронизации" */, NULL);
SetWaitableTimer(hTimer, &DueTime, Period, Beeper, &Count, TRUE);
while (!Exit) {
_tprintf(_T("Count = %d\n"), Count);
/* Значение счетчика увеличивается в процедуре таймера. */
/* Войти в состояние дежурного ожидания. */
SleepEx(INFINITE, TRUE);
}
_tprintf(_T("Завершение. Счетчик = %d"), Count);
CancelWaitableTimer(hTimer);
CloseHandle(hTimer);
return 0;
}
static VOID APIENTRY Beeper(LPVOID lpCount, DWORD dwTimerLowValue, DWORD dwTimerHighValue) {
*(LPDWORD)lpCount = *(LPDWORD)lpCount + 1;
_tprintf(_T("Генерация сигнала номер: %d\n"), *(LPDWORD) lpCount);
Веер(1000 /* Частота. */, 250 /* Длительность (мс). */);
return;
}
BOOL WINAPI Handler(DWORD CntrlEvent) {
Exit = TRUE;
_tprintf(_T("Завершение работы\n"));
return TRUE;
}