系统创建新线程时,会同时创建与这个线程相关联的队列,即异步过程调用(APC)的队列。
一些异步操作可以通过加入APC来实现,比如我现在学习的IO请求/完成。
BOOL ReadFileEx( HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine );
IO完成时,系统向该线程的APC队列中加入一项,包含lpCompleteionRoutine和lpOverlapped。当线程处于非执行态且是可提醒的状态时,系统会取出APC中的项,并让线程执行其中的回调函数。这个动作会重复到队列空,我猜想可能还会被线程正常唤醒打断。
非执行态是线程调用了等待、休眠函数,像
DWORD SleepEx(DWORD dwMilliseconds, bool bAlertable );DWORD WaitForSigleObjectEx(HANDLE hObject,DWORD dwMilliseconds,bool bAlertable);
bAlertable=true; 是可提醒状态!
另一段APC call的代码,是一个waitableTimer的例子。
#include <iostream> #include<process.h> #include<Windows.h> #include<tchar.h> #include<string.h> void APIENTRY TimerAPCRoutine(PVOID pvArgToCompleteRoutine, DWORD dwTimerLowValue, DWORD dwTimerHighValue); void SomeFunc() { HANDLE hTimer = CreateWaitableTimer(NULL, TRUE, NULL); LARGE_INTEGER li = { 0 }; SetWaitableTimer(hTimer, &li, 5000, TimerAPCRoutine, NULL, false); SleepEx(INFINITE, true); CloseHandle(hTimer); } void APIENTRY TimerAPCRoutine(PVOID pvArgToCompleteRoutine, DWORD dwTimerLowValue, DWORD dwTimerHighValue) { FILETIME ftUTC, ftLocal; SYSTEMTIME st; TCHAR szBuf[256]; ftUTC.dwHighDateTime = dwTimerHighValue; ftUTC.dwLowDateTime = dwTimerLowValue; FileTimeToLocalFileTime(&ftUTC, &ftLocal); FileTimeToSystemTime(&ftLocal, &st); GetDateFormat(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, szBuf, _countof(szBuf)); _tcscat_s(szBuf, _countof(szBuf), " "); GetTimeFormat(LOCALE_USER_DEFAULT, 0, &st, NULL, _tcschr(szBuf, TEXT('\0')), (int)(_countof(szBuf) - _tcslen(szBuf))); MessageBox(NULL, szBuf, TEXT("Timer went off at ..."), MB_OK); } int wmain(int argc, wchar_t* argv[]) { SomeFunc(); char c; std::cin >> c; return 0; }
线程跑到APC回调函数时,
总结:
APC是由系统管理的与线程相关的队列,可用来执行异步操作。
APC的回调函数是在原线程休眠时在原线程上调用。