Процессы в Windows
Страница 11. Ожидаемые таймеры



Ожидаемые таймеры

Пожалуй, ожидаемые таймеры - самый изощренный объект ядра для синхронизации. Появились они, начиная с Windows 98. Таймеры создаются функцией CreateWaitableTimer и бывают, также как и события, с автосбросом и без него. Затем таймер надо настроить функцией SetWaitableTimer. Таймер переходит в сигнальное состояние, когда истекает его таймаут. Отменить "тиканье" таймера можно функцией CancelWaitableTimer. Примечательно, что можно указать callback функцию при установке таймера. Она будет выполняться, когда срабатывает таймер.

Пример 8. Напишем программу-будильник используя WaitableTimer'ы. Будильник будет срабатыват раз в день в 8 утра и "пикать" 10 раз. Используем для этого два таймера, один из которых с callback-функцией.

#include <process.h>
#include <windows.h>
#include <stdio.h>
#include <conio.h>

#define HOUR (8) // время, когда срабатывает будильник (только часы)
#define RINGS (10) // сколько раз пикать

HANDLE hTerminateEvent ;

// callback функция таймера
VOID CALLBACK TimerAPCProc(LPVOID, DWORD, DWORD)
{
Beep(1000,500); // звоним!
};
 
// функция потока
unsigned __stdcall ThreadFunc(void *)
{
HANDLE hDayTimer = CreateWaitableTimer(NULL,FALSE,NULL);
HANDLE hAlarmTimer = CreateWaitableTimer(NULL,FALSE,NULL);
HANDLE h[2]; // мы будем ждать эти объекты
h[0] = hTerminateEvent; h[1] = hDayTimer;
int iRingCount=0; // число "звонков"
int iFlag;
DWORD dw;

// немного помучаемся со временем,
//т.к. таймер принимает его только в формате FILETIME

LARGE_INTEGER liDueTime, liAllDay;
liDueTime.QuadPart=0;
// сутки в 100-наносекундных интервалах = 10000000 * 60 * 60 * 24 = 0xC92A69C000
liAllDay.QuadPart = 0xC9;
liAllDay.QuadPart=liAllDay.QuadPart << 32;
liAllDay.QuadPart |= 0x2A69C000;
SYSTEMTIME st;
GetLocalTime(&st); // узнаем текущую дату/время
iFlag = st.wHour > HOUR; // если назначенный час еще не наступил,
//то ставим будильник на сегодня, иначе - на завтра

st.wHour = HOUR;
st.wMinute = 0;
st.wSecond =0;
FILETIME ft;
SystemTimeToFileTime( &st, &ft);
if (iFlag)
((LARGE_INTEGER *)&ft)->QuadPart =
((LARGE_INTEGER *)&ft)->QuadPart +liAllDay.QuadPart ;

LocalFileTimeToFileTime(&ft,&ft);
// Устанавливаем таймер,
// он будет срабатывать раз в сутки (24*60*60*1000ms),
// начиная со следующего "часа пик" - HOUR

SetWaitableTimer(hDayTimer, (LARGE_INTEGER *) &ft, 24*60*60000, 0, 0, 0);
do {
dw = WaitForMultipleObjectsEx(2,h,FALSE,INFINITE,TRUE);
if (dw == WAIT_OBJECT_0 +1) // сработал hDayTimer
{
// Устанавливаем таймер, он будет вызывать callback ф-ию раз в секунду,
// начнет с текущего момента

SetWaitableTimer(hAlarmTimer, &liDueTime, 1000, TimerAPCProc, NULL, 0);
iRingCount=0;
}
if (dw == WAIT_IO_COMPLETION) // закончила работать callback ф-ия
{
iRingCount++;
if (iRingCount==RINGS)
CancelWaitableTimer(hAlarmTimer);
}
}while (dw!= WAIT_OBJECT_0); // пока не сработало hTerminateEvent крутимся в цикле

// закрывае handles, выходим

CancelWaitableTimer(hDayTimer);
CancelWaitableTimer(hAlarmTimer);
CloseHandle(hDayTimer);
CloseHandle(hAlarmTimer);
_endthreadex( 0 );
return 0;
};

int main(int argc, char* argv[])
{
// это событие показывае потоку когда надо завершаться
hTerminateEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
unsigned uThreadID;
HANDLE hThread;
// создаем поток
hThread = (HANDLE)_beginthreadex( NULL, 0, &ThreadFunc, 0, 0,&uThreadID);
puts("Press any key to exit.");
// ждем any key от пользователя для завершения программы
getch();
// выставляем событие
SetEvent(hTerminateEvent);
// ждем завершения потока
WaitForSingleObject(hThread, INFINITE );
// закрываем handle
CloseHandle( hThread );
return 0;
}

 
« Предыдущая статья   Следующая статья »