以前第一次看到APC,也就是非同步程序呼叫的時候,沒有搞明白這是一個什麼樣的機制。
今天再看一次,然後結合一些簡單的測試代碼和搜尋來的資料,大概明白了是怎麼回事。
先來非常簡單地描述一下這一個“可等待的計時核心對象”。
大概定義如下:
可等待的計時器對象是這樣一種核心對象,它們會在某個指定的時間觸發,或每一段時間觸發一次,它們通常用來在某個時間執行一些操作。
相關的API(具體參數請參閱MSDN):
建立一個可等待的計時器:CreateWaitableTimer
得到一個已經存在的可等待的計時器的控制代碼:OpenWaitableTimer
設定可等待的計時器:SetWaitableTimer
可等待的計時器添加APC調用
Miscrosoft允許計時器把一個非同步程序呼叫(Asynchronnous procedure call,APC)放到調用線程的隊列中。將這一個非同步程序呼叫放到調用線程的隊列中,是通過調用SetWaitableTimer並利用類型為PTIMERAPCROUTINE的一個參數,這個參數其實是一個函數指標類型,所以非同步過程的地址就是傳到這裡。想要線程調用這些非同步過程,線程還必須處於可提醒狀態,也就是通過調用SleepEx,WaitForSingleObjectEx,WaitForMultipleObjectEx,MsgWaitForMultipleObjectEx而進入的等待狀態。這些函數都有一個參數,指是線程是否進入可提醒狀態。
範例程式碼:
#define _WIN32_WINNT 0x0400 #include<windows.h>#include<windowsx.h> //這裡面只包含一些宏,不包括一些類型定義#include<tchar.h>#include<stdio.h>VOID APIENTRY TimerAPCRoutine(PVOID pvArgToCompletionRoutine, DWORD dwTimerLowValue,DWORD dwTimerHighValue) ;int main(void){HANDLE hTimer ;LARGE_INTEGER li ; hTimer = CreateWaitableTimer(NULL,FALSE,NULL) ;const int nTimerUnitsPerSecond = 10000000 ;li.QuadPart = -(5 * nTimerUnitsPerSecond) ; //負值代表相對時間SetWaitableTimer(hTimer,&li,6*60*60*1000,TimerAPCRoutine,NULL,FALSE) ;printf("Main Thread Sleep for INFINE\n") ;SleepEx(INFINITE,TRUE) ;printf("Main Thread Wake Up\n") ;CloseHandle(hTimer) ;return 0 ;}VOID APIENTRY TimerAPCRoutine(PVOID pvArgToCompletionRoutine,DWORD dwTimerLowValue,DWORD dwTimerHighValue) {printf("APC Fuction\n") ;return ;}
程式大概的執行流程就是:建立一個可等待的計時器並添加一個非同步程序呼叫,隨之調用SleepEx進入可提醒狀態。當計時器觸發時,調用SetWaitableTimer的線程就會調用APC隊列中的一項,也就是我們剛才傳入的函數地址。執行完畢後,線程隨即從SleepEx調用中返回。大概流程就是這樣。
PS:如果調用SleepEx時,APC隊列不為空白,則線程不會睡眠,而是調用APC隊列中的一項,直到APC隊列為空白。當調用那些讓線程睡眠的函數時,若且唯若線程的APC隊列中一項都沒有的時候,這些函數才會將線程掛起。
還值得一提的是,這一個讓可等待的計時器添加APC調用的機制,有點類似Linux下的處理alarm訊號的訊號處理函數。