以前對User-Mode APC不甚了解, 最近在看一個開源項目時看到了對APC的使用。看來多看代碼的確是有好處地:)廢話不多說。我們來看看APC的真面目吧。
APC即asynchronous procedure call,每一線程都有一個APC隊列。操作系統 允許一個應用向一個指定線程的APC隊列中放入APC函數。當指定的線程處於警告狀態時,該線程就會調用隊列中的APC函數。調用的順序為先入先出(FIFO)。可以用以下函數使一個線程進入警告狀態:
SleepEx, SignalObjectAndWait, WaitForSingleObjectEx, WaitForMultipleObjectsEx, MsgWaitForMultipleObjectsEx
一句話,就是可以讓別的線程執行一個函數。下面是一段例子
LRESULT CALLBACK APCWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
if (msg==WM_NULL) SleepEx(0,TRUE);
return DefWindowProc(hwnd,msg,wParam,lParam);
}
//主線程調用
BOOL Initialize()
{
DuplicateHandle(GetCurrentProcess(),
GetCurrentThread(),
GetCurrentProcess(),
&g_hMainThread,
THREAD_SET_CONTEXT,
FALSE,
0);
//為了讓APC盡快響應,創建窗口,在WM_NULL中調用SleepEx讓線程進入警告狀態。
g_hAPCWnd = CreateWindowEx(0,_T("STATIC"),NULL,0, 0,0,0,0, NULL,NULL,NULL,NULL);
SetWindowLongPtr(m_hACPWnd, GWL_WNDPROC, (LONG)(LONG_PTR)APCWndProc);
return TRUE;
}
//工作者線程調用
int CallFunctionAsync(void (__stdcall *func)(void *), void *arg)
{
int res = 0;
res = QueueUserAPC((void (__stdcall *)(DWORD))func, g_hMainThread, (DWord_PTR)arg);
PostMessage(g_hAPCWnd, WM_NULL, 0, 0);
return res;
}
主線程調用Initialize來創建APCWnd,以及保存主線程句柄。為了使APC能夠及時響應創建APCWnd,工作者線程調用CallFunctionAsync讓主線程運行 func函數指針指向的函數,在該函數中向APCWnd發送WM_NULL消息,窗口過程在響應該消息時調用SleepEx使線程進入警告狀態,從而檢查並調用APC隊列中的APC函數。這樣就實現了一個讓已知的線程調用指定的函數。