通過異步程序調(diào)用(APC)實現(xiàn)的定時功能
定時器是一個在特定時間或者規(guī)則間隔被激發(fā)的內(nèi)核對象。結(jié)合定時器的異步程序調(diào)用可以允許回調(diào)函數(shù)在任何定時器被激發(fā)的時候執(zhí)行。本文的例子代碼顯示了如何實現(xiàn)。
使用本定時器時,你需要把常量_WIN32_WINNT定義為0x0400,并且此常量應(yīng)該在包之前定義,以確保聲明合適的定時器原型函數(shù)。
通過調(diào)用CreateWaitableTimer()可以創(chuàng)建一個定時器,此函數(shù)返回一個指向內(nèi)核對象的句柄。若定時器已經(jīng)存在,你可以通過使用OpenWaitableTimer()獲得一個進程相關(guān)的句柄。無論是通過CreateWaitableTimer() 還是通過OpenWaitableTimer()獲得的句柄,在不需要定時器時必須釋放,方法是使用函數(shù)CloseHandle()。
定時的時間通過調(diào)用SetWaitableTimer()來設(shè)置,可以設(shè)置為一個特定的時刻(如December 16, 1999 at 9:45 PM)或者一個相對的時間(如從現(xiàn)在起每五分鐘)。函數(shù)SetWaitableTime()定時的時間參數(shù)要求LARGE_INTEGER類型。這個值應(yīng)該符合在結(jié)構(gòu)體FILETIME中描述的格式。如果值是正的,代表一個特定的時刻。如果值是負(fù)的,代表以100納秒為單位的相對時間。后面的示例代碼中使用的是相對時間。在調(diào)用SetWaitableTimer()函數(shù)后,定時器將在每5秒被激發(fā)一次。
你也可以將定時器設(shè)置為周期性的自我激發(fā),方法是向SetWaitableTimer()的第三個參數(shù)傳遞一個周期參數(shù)(以毫秒為單位)。在CreateWaitableTimer()的第二個參數(shù)傳遞FALSE可以產(chǎn)生一個自動歸零的定時器。本例設(shè)置周期為兩秒的定時器。
當(dāng)設(shè)置了定時器之后,你就可以將APC與其結(jié)合起來。這里把APC函數(shù)稱作完全例程。完全例程的地址作為SetWaitableTimer()的第四個參數(shù)。第五個參數(shù)是一個空類型的指針,你可以使用它來傳遞完全例程的參數(shù)。
在所有的APC中,要執(zhí)行一個完全例程則線程必須處于監(jiān)聽狀態(tài)。完全例程將總是被調(diào)用SetWaitableTimer()的相同的線程執(zhí)行,所以此線程必須將必須其自身置于監(jiān)聽狀態(tài)??梢哉{(diào)用下面的任何一個監(jiān)聽函數(shù)來完成監(jiān)聽狀態(tài)的設(shè)置:
使用本定時器時,你需要把常量_WIN32_WINNT定義為0x0400,并且此常量應(yīng)該在包之前定義,以確保聲明合適的定時器原型函數(shù)。
通過調(diào)用CreateWaitableTimer()可以創(chuàng)建一個定時器,此函數(shù)返回一個指向內(nèi)核對象的句柄。若定時器已經(jīng)存在,你可以通過使用OpenWaitableTimer()獲得一個進程相關(guān)的句柄。無論是通過CreateWaitableTimer() 還是通過OpenWaitableTimer()獲得的句柄,在不需要定時器時必須釋放,方法是使用函數(shù)CloseHandle()。
定時的時間通過調(diào)用SetWaitableTimer()來設(shè)置,可以設(shè)置為一個特定的時刻(如December 16, 1999 at 9:45 PM)或者一個相對的時間(如從現(xiàn)在起每五分鐘)。函數(shù)SetWaitableTime()定時的時間參數(shù)要求LARGE_INTEGER類型。這個值應(yīng)該符合在結(jié)構(gòu)體FILETIME中描述的格式。如果值是正的,代表一個特定的時刻。如果值是負(fù)的,代表以100納秒為單位的相對時間。后面的示例代碼中使用的是相對時間。在調(diào)用SetWaitableTimer()函數(shù)后,定時器將在每5秒被激發(fā)一次。
你也可以將定時器設(shè)置為周期性的自我激發(fā),方法是向SetWaitableTimer()的第三個參數(shù)傳遞一個周期參數(shù)(以毫秒為單位)。在CreateWaitableTimer()的第二個參數(shù)傳遞FALSE可以產(chǎn)生一個自動歸零的定時器。本例設(shè)置周期為兩秒的定時器。
當(dāng)設(shè)置了定時器之后,你就可以將APC與其結(jié)合起來。這里把APC函數(shù)稱作完全例程。完全例程的地址作為SetWaitableTimer()的第四個參數(shù)。第五個參數(shù)是一個空類型的指針,你可以使用它來傳遞完全例程的參數(shù)。
在所有的APC中,要執(zhí)行一個完全例程則線程必須處于監(jiān)聽狀態(tài)。完全例程將總是被調(diào)用SetWaitableTimer()的相同的線程執(zhí)行,所以此線程必須將必須其自身置于監(jiān)聽狀態(tài)??梢哉{(diào)用下面的任何一個監(jiān)聽函數(shù)來完成監(jiān)聽狀態(tài)的設(shè)置:
- SleepEx();
- WaitForSingleObjectEx();
- WaitForMultipleObjectsEx();
- MsgWaitForMultipleObjectsEx();
- SignalObjectAndWait();
任何一個線程都有一個APC隊列。在調(diào)用上面的任何一個函數(shù)時,如果線程的APC隊列中有實體,則此線程不會進入休眠狀態(tài),取而代之要做的是將實體從APC隊列中取出,然后調(diào)用相應(yīng)的完全例程。
如果在APC隊列中不存在實體,那么線程將會被掛起,直至等待條件滿足為止。滿足等待條件的有:一個實體加入到APC隊列中,超時,激活句柄等,以及在調(diào)用MsgWaitForMultipleObjectsEx()情況下,一個消息進入到線程的一個消息隊列中。若等待條件滿足的是APC隊列中的一個實體,那么線程會被激活,并且執(zhí)行完全例程,這種情況下的函數(shù)的返回值是 WAIT_IO_COMPLETION.
如果在APC隊列中不存在實體,那么線程將會被掛起,直至等待條件滿足為止。滿足等待條件的有:一個實體加入到APC隊列中,超時,激活句柄等,以及在調(diào)用MsgWaitForMultipleObjectsEx()情況下,一個消息進入到線程的一個消息隊列中。若等待條件滿足的是APC隊列中的一個實體,那么線程會被激活,并且執(zhí)行完全例程,這種情況下的函數(shù)的返回值是 WAIT_IO_COMPLETION.
【重要提示】
1、在執(zhí)行完一個完全例程之后,系統(tǒng)會檢查在APC中剩下的實體以處理。一個監(jiān)視函數(shù)僅僅在處理完所有APC實體后才返回。因此,如果實體加入到APC隊列的速度比處理的更快的話,則調(diào)用這些函數(shù)可能永遠也不能返回。特別當(dāng)定時等待的時間比起要求執(zhí)行完全例程的時間更短的話,這種情況更容易發(fā)生。
2、當(dāng)使用APC來實現(xiàn)定時器時,設(shè)置定時的線程不應(yīng)該等待定時器的句柄。如果等待定時器的句柄的話,則喚起這個線程的原因是定時器被激活,而不是有實體加入到APC隊列中。這時線程將不再處于監(jiān)聽狀態(tài),所以完全例程也不會被調(diào)用。在本例中,Sleep()被用于將線程置于監(jiān)聽狀態(tài)。在定時器激活后,如果有實體被加入到此線程的APC隊列中時,Sleep()就會喚醒此線程。
2、當(dāng)使用APC來實現(xiàn)定時器時,設(shè)置定時的線程不應(yīng)該等待定時器的句柄。如果等待定時器的句柄的話,則喚起這個線程的原因是定時器被激活,而不是有實體加入到APC隊列中。這時線程將不再處于監(jiān)聽狀態(tài),所以完全例程也不會被調(diào)用。在本例中,Sleep()被用于將線程置于監(jiān)聽狀態(tài)。在定時器激活后,如果有實體被加入到此線程的APC隊列中時,Sleep()就會喚醒此線程。
【示例代碼】
#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <stdio.h>
#define _SECOND 10000000
typedef struct _MYDATA {
TCHAR *szText;
DWORD dwValue;
} MYDATA;
VOID CALLBACK TimerAPCProc(
LPVOID lpArg, // Data value
DWORD dwTimerLowValue, // Timer low value
DWORD dwTimerHighValue ) // Timer high value
{
MYDATA *pMyData = (MYDATA *)lpArg;
printf( "Message: %s\nValue: %d\n\n", pMyData->szText,
pMyData->dwValue );
MessageBeep(0);
}
void main( void )
{
HANDLE hTimer;
BOOL bSuccess;
__int64 qwDueTime;
LARGE_INTEGER liDueTime;
MYDATA MyData;
TCHAR szError[255];
MyData.szText = "This is my data.";
MyData.dwValue = 100;
if ( hTimer = CreateWaitableTimer(
NULL, // Default security attributes
FALSE, // Create auto-reset timer
"MyTimer" ) ) // Name of waitable timer
{
__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
{
wsprintf( szError, "SetWaitableTimer failed with Error \
%d.", GetLastError() );
MessageBox( NULL, szError, "Error", MB_ICONEXCLAMATION );
}
}
__finally
{
CloseHandle( hTimer );
}
}
else
{
wsprintf( szError, "CreateWaitableTimer failed with Error %d.",
GetLastError() );
MessageBox( NULL, szError, "Error", MB_ICONEXCLAMATION );
}
}
posted on 2008-05-09 17:20 肥仔 閱讀(617) 評論(0) 編輯 收藏 引用 所屬分類: MFC存檔

