waitable timer
顧名思義,就是隔一段時(shí)間被signaled的一種內(nèi)核對(duì)象。waitable timer跟event對(duì)象一樣可以在創(chuàng)建的時(shí)候指定reset方式,如果是manual-reset,那么當(dāng)waitable timer對(duì)象被signaled時(shí),所有等待這個(gè)對(duì)象的wait函數(shù)都會(huì)返回。如果是auto-reset那么就只有一個(gè)wait函數(shù)會(huì)返回。
創(chuàng)建完waitable timer對(duì)象后,必須通過SetWaitableTimer函數(shù)對(duì)它進(jìn)行時(shí)間上的設(shè)置。時(shí)間格式是個(gè)問題,看下面代碼
// Declare our local variables.
HANDLE hTimer;
SYSTEMTIME st;
FILETIME ftLocal, ftUTC;
LARGE_INTEGER liUTC;

// Create an auto-reset timer.
hTimer = CreateWaitableTimer(NULL, FALSE, NULL);

// First signaling is at January 1, 2002, at 1:00 P.M. (local time).
st.wYear = 2002; // Year
st.wMonth = 1; // January
st.wDayOfWeek = 0; // Ignored
st.wDay = 1; // The first of the month
st.wHour = 13; // 1PM
st.wMinute = 0; // 0 minutes into the hour
st.wSecond = 0; // 0 seconds into the minute
st.wMilliseconds = 0; // 0 milliseconds into the second

SystemTimeToFileTime(&st, &ftLocal);

// Convert local time to UTC time.
LocalFileTimeToFileTime(&ftLocal, &ftUTC);
// Convert FILETIME to LARGE_INTEGER because of different alignment.
liUTC.LowPart = ftUTC.dwLowDateTime;
liUTC.HighPart = ftUTC.dwHighDateTime;

// Set the timer.
SetWaitableTimer(hTimer, &liUTC, 6 * 60 * 60 * 1000, NULL, NULL, FALSE);
上面的代碼查下MSDN應(yīng)該很容易理解,這里要說的是CPU對(duì)齊的問題。FILETIME結(jié)構(gòu)必須位于32位邊界,而LARGE_INTEGER必須位于64位邊界,所以不能將FILETIME直接傳給SetWaitableTimer。
SetWaitableTimer也可以使用時(shí)間的絕對(duì)值,或者使用相對(duì)時(shí)間值。不過這時(shí)的值必須是負(fù)的。看下面代碼:
// Declare our local variables.
HANDLE hTimer;
LARGE_INTEGER li;

// Create an auto-reset timer.
hTimer = CreateWaitableTimer(NULL, FALSE, NULL);

// Set the timer to go off 5 seconds after calling SetWaitableTimer.
// Timer unit is 100-nanoseconds.
const int nTimerUnitsPerSecond = 10000000;

// Negate the time so that SetWaitableTimer knows we
// want relative time instead of absolute time.
// This indicate that the timer will be signaled 5 seconds after the call to SetWaitableTimer
li.QuadPart = -(5 * nTimerUnitsPerSecond);
// Set the timer.
SetWaitableTimer(hTimer, &li, 6 * 60 * 60 * 1000, NULL, NULL, FALSE);
清除waitable timer對(duì)象需要用到CancelWaitableTimer函數(shù)。
特別提出的是waitable timer這節(jié)引出了一個(gè)新概念:APC(asynchronous procedure call)。按照我的理解,APC應(yīng)該是線程特有的一個(gè)隊(duì)列,里面裝的是函數(shù)地址。如果一個(gè)函數(shù)地址被裝入APC,如果這時(shí)線程處于待命的等待狀態(tài)(alertable wait),那么這個(gè)線程就會(huì)被喚醒去調(diào)用APC里的函數(shù);否則,APC里的函數(shù)地址就會(huì)被忽略掉。這里的這個(gè)線程指的是調(diào)用SetWaitableTimer的線程。下面的代碼能說明問題
VOID APIENTRY TimerAPCRoutine(PVOID pvArgToCompletionRoutine,

DWORD dwTimerLowValue, DWORD dwTimerHighValue)
{

FILETIME ftUTC, ftLocal;
SYSTEMTIME st;
TCHAR szBuf[256];

// Put the time in a FILETIME structure.
ftUTC.dwLowDateTime = dwTimerLowValue;
ftUTC.dwHighDateTime = dwTimerHighValue;

// Convert the UTC time to the user's local time.
FileTimeToLocalFileTime(&ftUTC, &ftLocal);

// Convert the FILETIME to the SYSTEMTIME structure
// required by GetDateFormat and GetTimeFormat.
FileTimeToSystemTime(&ftLocal, &st);

// Construct a string with the
// date/time that the timer went off.
GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE,
&st, NULL, szBuf, sizeof(szBuf) / sizeof(TCHAR));
_tcscat(szBuf, _ _TEXT(" "));
GetTimeFormat(LOCALE_USER_DEFAULT, 0,
&st, NULL, _tcschr(szBuf, 0),
sizeof(szBuf) / sizeof(TCHAR) - _tcslen(szBuf));

// Show the time to the user.
MessageBox(NULL, szBuf, "Timer went off at
", MB_OK);
}


void SomeFunc()
{
// Create a timer. (It doesn't matter whether it's manual-reset
// or auto-reset.)
HANDLE hTimer = CreateWaitableTimer(NULL, TRUE, NULL);

// Set timer to go off in 5 seconds.

LARGE_INTEGER li =
{ 0 };
SetWaitableTimer(hTimer, &li, 5000, TimerAPCRoutine, NULL, FALSE);

// Wait in an alertable state for the timer to go off.
SleepEx(INFINITE, TRUE);

CloseHandle(hTimer);
}
如果指定了APC,那么就不要等待這個(gè)waitable timer對(duì)象了,因?yàn)锳PC隊(duì)列會(huì)喚醒線程的,不需要wait函數(shù)。