以前对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函数。这样就实现了一个让已知的线程调用指定的函数。