一 参考
https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-queueuserapc
<<windows核心编程>> 第5版
二 每个线程都有一个APC队列, 在线程处于可警醒状态时, 线程会执行APC队列中apc函数
经过以上参考文章加上测试发现, APC队列中的函数会在以下3个时机调用
1 线程已经创建, 系统在调用线程函数时会检查APC队列, 如果不为空, 则调用APC队列中的apc函数, 直到队列为空后, 才开始调用线程函数
2 线程通过WaitForSingleObjectEx等函数进入可警醒状态时, 会先检查APC队列, 如果不为空, 则调用APC队列中的apc函数, 直到队列为空后, 才开始等待要等待的对象, 此时如果要等待的对象没有进入激发态且没有超时WaitForSingleObjectEx不会返回
3 线程通过WaitForSingleObjectEx等函数进入可警醒状态时, APC队列为空, 且要等待的对象没有进入激发态, 也没有超时, 则线程进入睡眠等待状态, 此时往该APC队列添加APC函数后, 该线程会被唤醒执行完所有APC队列中的函数, 然后不去看要等待的对象是否进入激发态, 立即从WaitForSingleObjectEx中返回, 返回值是WAIT_IO_COMPLETION
测试代码如下
1 #include <windows.h> 2 #include <iostream> 3 #include <process.h> 4 5 HANDLE hEvent = NULL; 6 HANDLE HMainEvent = NULL; 7 DWORD WINAPI TestProc(LPVOID lpThreadParameter) 8 { 9 printf("thread start "); 10 SetEvent(HMainEvent); 11 if (hEvent && hEvent != INVALID_HANDLE_VALUE) 12 { 13 printf("waitfor start "); 14 DWORD ret = WaitForSingleObjectEx(hEvent, INFINITE, TRUE); 15 //WAIT_OBJECT_0 16 printf("waitfor ret= 0x%08x ", ret); 17 } 18 printf("thread end "); 19 return 0; 20 } 21 VOID NTAPI TestAPC(ULONG_PTR Parameter) 22 { 23 printf("apc func start index=%u ",Parameter); 24 } 25 int main(int argc, char **argv, char **env) 26 { 27 HANDLE hT = NULL; 28 hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 29 HMainEvent = CreateEvent(NULL, TRUE, FALSE, NULL); 30 31 if (hEvent) 32 { 33 hT = (HANDLE)_beginthreadex(NULL, 0, (_beginthreadex_proc_type)TestProc, NULL, 0, NULL); 34 if (hT && hT != INVALID_HANDLE_VALUE) 35 { 36 WaitForSingleObject(HMainEvent, INFINITE); 37 //Sleep(2000); 38 ULONG_PTR index = 1; 39 for(;index <100;++index) 40 { 41 QueueUserAPC(TestAPC, hT, index); 42 } 43 Sleep(2000); 44 SetEvent(hEvent); 45 WaitForSingleObject(hT, INFINITE); 46 } 47 } 48 CloseHandle(hEvent); 49 CloseHandle(HMainEvent); 50 CloseHandle(hT); 51 system("pause"); 52 return 0; 53 }