1. 程式人生 > >【Windows原理】非同步IO-_APC(非同步過程呼叫)

【Windows原理】非同步IO-_APC(非同步過程呼叫)

// 同步IO的缺點是, 在讀寫檔案時, 如果檔案太大, 或者讀寫的時間太長, 就會在讀寫函式中
// 阻塞住.  
// 非同步IO解決了這個問題, 非同步IO讀寫檔案時, 檔案再大也不會阻塞住
// 但是非同步IO要完成這樣的特性是有一點付出的
// 非同步讀寫檔案後, 需要通過一些方式才能知道檔案讀寫(IO任務)什麼時候完成. 
// 這裡講的是第三個方式, 通過設定IO完成函式來處理已完成的IO任務.
// 在Windows 中, ReadFile和WriteFile還有另外一個版本的函式, 那就是ReadFileEx 和
// WriteFileEx, 這兩個增強版的函式中, 都是添加了一個引數, 這個引數是一個函式指標.
// 當I/O任務完成之後, 系統內部會在一段時間內呼叫這個函式指標. 但是系統並不是無條件調 // 用該函式指標的. // 每個執行緒都有一個APC佇列(非同步過程呼叫佇列), 這個佇列實際上就是一個數組, 這個陣列中 // 的每一個元素都是一個函式的地址. 執行緒被掛起時, 這些函式會被系統呼叫, 系統會從APC隊 // 列中移除已經被呼叫過的函式. // 執行緒掛起時, 預設是不執行APC佇列中的函式的, 想要線上程掛起時讓系統自動呼叫APC佇列中 // 的函式, 必須使用以下的函式來掛起執行緒: // 1. SleepEx // 2. WaitForSignalObjectEx // 3. WaitForMultipleObjectEx
// 4. SignalObjectAndWait // 5. GetQueuedCompletionStatusEx // 6. MsgWaitMultipelObjectEx // 這些函式中,有一個引數 BOOL bAlertble 這個引數就用於指定是否呼叫APC佇列中的函式 // 的.非同步I/O中的可提醒I/O通知方式實際的原理是這樣的:當I/O任務完成後,系統會將完成函 // 數的地址傳送到執行緒APC佇列( 通過QueueUserAPC 傳送 ),然後當執行緒被指定的函式掛起 // 後 ,可提醒I/O的完成函式才會被呼叫. #include "stdafx.h" #include <windows.h>
#include <process.h> struct MyOVERLAPPED:public OVERLAPPED { public: char * pBuff; // 用於儲存緩衝區首地址 int nIndex; // 用於儲存IO任務的序號 MyOVERLAPPED(); MyOVERLAPPED(int nSize, int nFileOffsetLow, int nFileOffsetHeight = 0); ~MyOVERLAPPED(); }; // 完成函式 VOID WINAPI OverlappedCompliteFun( DWORD dwErrorCode, // 錯誤碼 DWORD dwNumberOfBytesTransfered, // 成功讀寫的位元組數 LPOVERLAPPED lpOverlapped // 重疊IO結構體指標 ) { MyOVERLAPPED* pMyOverlapped = (MyOVERLAPPED*)lpOverlapped; printf("[%d]IO任務完成,讀取位元組:%d,讀取位置:%d,讀取內容:[%s]\n",pMyOverlapped->nIndex,pMyOverlapped->Internal,pMyOverlapped->Offset,pMyOverlapped->pBuff); // 釋放空間 delete pMyOverlapped; } int _tmain(int argc, _TCHAR* argv[]) { HANDLE hFile = CreateFile(L"1.txt",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,NULL); if (hFile == INVALID_HANDLE_VALUE)return 0; int nFileRead = 50; int nReadPos = 0; // 第一個IO任務 MyOVERLAPPED * vo = new MyOVERLAPPED(nFileRead,nReadPos); vo->nIndex = 0; ReadFileEx(hFile, vo->pBuff, nFileRead, vo, OverlappedCompliteFun); // 第二個IO任務 nFileRead = 80; nReadPos = 500; MyOVERLAPPED * vo2 = new MyOVERLAPPED(nFileRead,nReadPos); vo2->nIndex = 1; // 任務序號 ReadFileEx(hFile, vo2->pBuff, nFileRead, vo2, OverlappedCompliteFun); // 如果將下面這行程式碼註釋掉,完成函式將不會被呼叫 SleepEx(3, TRUE); system("pause"); return 0; } // 預設建構函式 MyOVERLAPPED::MyOVERLAPPED() :pBuff(nullptr) { OVERLAPPED::hEvent = 0; } // 初始化建構函式 MyOVERLAPPED::MyOVERLAPPED(int nIoSIze, int nFileOffsetLow, int nFileOffsetHeight) { // 建立事件物件 OVERLAPPED::hEvent = 0; // 不需要事件物件 // 儲存檔案讀寫偏移 OVERLAPPED::Offset = nFileOffsetLow; OVERLAPPED::OffsetHigh = nFileOffsetHeight; // 申請緩衝區儲存檔案內容 pBuff = new char[nIoSIze]{}; } // 解構函式,釋放空間,事件物件 MyOVERLAPPED::~MyOVERLAPPED() { // 釋放空間 if (pBuff != nullptr) { delete[] pBuff; pBuff = nullptr; } }