
1. Создание объекта "ожидаемый таймер" производится следующей функцией:
HANDLE CreateWaitableTimer( LPSECURITY_ATTRIBUTES lpWTimerAttrib, BOOL fManualReset, LPCTSTR lpName);
Параметры функции:
- lpWTimerAttrib является указателем на структуру типа SECURITY_ATTRIBUTES, если не используется, то задается NULL;
- fManualReset указывает, какого типа таймер будет создан: со сбросом вручную (TRUE) или с автосбросом (FALSE);
- lpName указывает на строку, содержащую имя таймера, которое необходимо для доступа к объекту других процессов. Если не нужен именованный объект, то указывается NULL.
Функция CreateWaitableTimer возвращает описатель таймера, используемый для работы с ним.
Объекты «ожидаемый таймер» всегда создаются в занятом состоянии.
2. Задание времени и периодичности, когда ожидаемый таймер будет переходить в свободное состояние производится следующей функцией:
BOOL SetWaitableTimer( HANDLE hWTimer,
const LARGE_INTEGER *pDueTime, LONG lPeriod,
PTIMERAPCROUTINE pfnCompletionRoutine,
PVOID pvArgToCompletionRoutine, BOOL fResume);
где
- hWTimer – дескриптор ожидаемого таймера, возвращенный функцией CreateWaitableTimer;
- pDueTime указывает на целое 64-битное значение, где хранится дата и время (в формате UTC - Coordinated Universal Time) первого перехода таймера в свободное состояние. Если это значение имеет отрицательный знак, то его абсолютная величина указывает, через какой промежуток (в интервалах по 100 нс) после вызова данной функции таймер станет свободным;
- lPeriod задает периодичность (в миллисекундах) следующих переходов таймера в свободное состояние;
- pfnCompletionRoutine и pvArgToCompletionRoutine задают имя APC-функции, вызываемой при переходе таймера в свободное состояние и адрес передаваемых ей данных. APC – это асинхронный вызов функций (Asynchronous Procedure Call). Если такая функция не используется, то в обеих параметрах указываются NULL;
- fResume используется на компьютерах, поддерживающих режим сна. Если он равен TRUE, то при срабатывании таймера компьютер выйдет из режима сна и "разбудит" потоки, ожидающие этот таймер. В противном случае объект-таймер перейдет в свободное состояние, но ожидавшие его потоки не получат процессорное время, пока компьютер не выйдет из режима сна.
Функция возвращает TRUE, если ее вызов выполнен успешно и FALSE в противном случае.
3. Отключение срабатываний таймера производится функцией:
BOOL CancelWaitableTimer(HANDLE hWTimer);
После этого восстановить работу таймера можно повторным вызовом функции SetWaitableTimer() с заданием новых времени и периодичности.
4. Уничтожение объекта hWTimer и освобождение его дескриптора производится функцией:
BOOL CloseHandle(HANDLE hWTimer);
Взаимодействие потоков с ожидаемыми таймерами происходит следующим образом.
После создания ожидаемого таймера, он должен быть настроен на первое срабатывание функцией SetWaitableTimer(). При наступлении заданного момента времени ожидаемый таймер переходит в свободное состояние и в зависимости от его типа происходит следующее:
1) если таймер с автосбросом, то он автоматически сбрасывается в занятое состояние, при этом только один поток будет работать, а остальные потоки останутся ждать. Для возобновления работы потоков, ожидающих данного таймера необходимо вызвать функцию SetWaitableTimer(), задав новое время его срабатывания;
2) если таймер со сбросом вручную, то все потоки, ожидающие этот таймер, получат управление, а он так и останется в свободном состоянии, пока какой-нибудь поток не вызовет CancelWaitableTimer(). После этого возобновить работу таймера можно вызовом функции SetWaitableTimer() с заданием новых времени и периодичности.
Ожидаемые таймеры отличаются от обычных таймеров Windows (работающих в пользовательском режиме и настраиваемых функцией SetTimer()) следующим: во-первых они являются объектами ядра, а значит более защищенными; во-вторых они более точные, т.к. сообщения WM_TIMER, посылаемые обычными таймерами имеют низкий приоритет и в очереди сообщений обрабатываются последними.
Примечание: рассмотренные выше функции Win32 API для работы с ожидаемыми таймерами появились позже других функций для синхронизации потоков, начиная с версий Windows NT 4.0 и Windows 98. Поэтому описания прототипов этой группы функций в заголовочном файле WINBASE.H заключены в блок условной компиляции:
#if (_WIN32_WINNT >= 0x0400) || (_WIN32_WINDOWS > 0x0400)
… // Прототипы функций для работы с ожид. таймерами
#endif
Тем самым ограничивается возможность использования этих функций в ранних версиях ОС Windows.
При работе с ожидаемыми таймерами в программе необходимо перед включением заголовочного файла StdAfx.h определить один из указанных в WINBASE.H идентификаторов:
#define _WIN32_WINNT 0x0400
#include <stdafx.h>
Примеры использования ожидаемых таймеров. (См. электронный вар-т)
Пример 1. Два потока синхронизируются объектом "ожидаемый таймер" с автосбросом. Время срабатывания таймера является относительным и задается в количестве интервалов по 100 нс. Первое срабатывание – через 10 мс после создания таймера, последующие – через 2 мс после завершения потоком своих операций. (1 мс равна 10000 интервалов по 100 нс).
#define _WIN32_WINNT 0x0400
…
HANDLE hWTimer;
LARGE_INTEGER li;
hWTimer=CreateWaitableTimer(NULL, FALSE, NULL);
// Задаем отрицательное число в количестве
// интервалов по 100 нс
li.QuadPart = - (10 * 10000);
// Устанавливаем первое срабатывание через 10 мс
SetWaitableTimer(hWTimer,&li, 0, NULL, NULL, FALSE);
…
// Функция потока 1
DWORD WINAPI ThreadFunc1(PVOID pvParam)
{
…
while (!p->stop)
{
// ожидание освобождения таймера hWTimer
WaitForSingleObject(hWTimer, INFINITE);
// выполнение операций потоком 1
...
// Задаем следующее срабатывание через 2 мс
li.QuadPart = - (2 * 10000);
SetWaitableTimer(hWTimer,&li,0,NULL,NULL,FALSE);
}
…
}
// Функция потока 2
DWORD WINAPI ThreadFunc2(PVOID pvParam)
{
…
while (!p->stop)
{
// ожидание освобождения таймера hWTimer
WaitForSingleObject(hWTimer, INFINITE);
// выполнение операций потоком 2
...
// Задаем следующее срабатывание через 2 мс
li.QuadPart = - (2 * 10000);
SetWaitableTimer(hWTimer,&li,0,NULL,NULL,FALSE);
}
…
}
Пример 2. Устанавливаем ожидаемый таймер, чтобы он срабатывал ежедневно в 12.00 (время перерыва на обед) и вызывал APC-функцию, которая выдает звуковой сигнал 1000 гц.
#define _WIN32_WINNT 0x0400
…
HANDLE hWTimer;
SYSTEMTIME st;
FILETIME ftLocal, ftUTC;
LARGE_INTEGER liUTC;
// callback функция таймера
VOID CALLBACK TimerAPCProc(LPVOID, DWORD, DWORD)
{
Beep(1000,500); // выдается звуковой сигнал
};
...
// создаем таймер с автосбросом
hWTimer = CreateWaitableTimer(NULL, FALSE, NULL);
// узнаем текущую дату/время
GetLocalTime(&st);
// если назначенный час уже наступил,
// то ставим время на завтра
if (st.wHour > 12) st.wDay++;
st.wHour = 12;
st.wMinute = 0;
st.wSecond = 0;
// преобразуем время из SYSTEMTIME в FILETIME
SystemTimeToFileTime(&st, &ftLocal);
// преобразуем местное время в UTC-время
LocalFileTimeToFileTime(&ftLocal, &ftUTC);
// преобразуем FILETIME в LARGE_INTEGER из-за
// различий в выравнивании данных
liUTC.LowPart = ftUTC.dwLowDateTime;
liUTC.HighPart = ftUTC.dwHighDateTime;
// устанавливаем таймер
SetWaitableTimer(hWTimer, &liUTC, 24*60*60*1000, TimerAPCProc, NULL, FALSE);
. . .