1. 程式人生 > >【Windows原理】IO非同步-等待事件物件

【Windows原理】IO非同步-等待事件物件

/*

同步IO的缺點是, 在讀寫檔案時, 如果檔案太大, 或者讀寫的時間太長, 就會在讀寫函式中
阻塞住.  
非同步IO解決了這個問題, 非同步IO讀寫檔案時, 檔案再大也不會阻塞住
但是非同步IO要完成這樣的特性是有一點付出的
非同步讀寫檔案後, 需要通過一些方式才能知道檔案讀寫(IO任務)什麼時候完成. 
這裡講的是第二個方式, 通過等待事件物件的訊號來處理已完成的IO任務
非同步IO中, 無論是ReadFile還是WriteFile , 最後一個引數都需要傳遞一個OVERLAPPED
結構體變數的地址, 這個結構體中, 有一個hEvent的欄位. 這個欄位就是完成事件物件通知
的依賴.
IO任務完成以後, 系統會檢查OVERLAPPED結構體中的hEveht欄位是否為NULL,如果不為NULL
系統會呼叫SetEvent將hEveht設定為有訊號. 
因此, 我們想要利用這種方式, 就會在派發IO任務之前, 建立一個事件物件, 並把它賦值到
OVERLAPPED結構體中的hEveht欄位中, 在派發IO任務之後, 建立一個執行緒,或者在某個位置
中呼叫WaitForMultipleObjects來等待事件物件的訊號的就可以了.

*/
#include "stdafx.h" #include <windows.h> #include <process.h> /* * 繼承OVERLAPPED * 因為OVERLAPPED結構體中的欄位並沒有儲存檔案讀寫內容緩衝區的首地址 * 在原有的結構體基礎之上想要擴充套件新的功能,在這裡使用了C++的繼承特性 * MyOverlapped前面的部分是OverLAPPED的欄位 * 後面的部分是新新增的成員 **/ struct MyOVERLAPPED:public OVERLAPPED { public: char * pBuff; // 用於儲存緩衝區首地址
int nIndex; // 用於儲存IO任務的序號 MyOVERLAPPED(); MyOVERLAPPED(int nIoSize,int nFileOffsetLow,int nFileOffsetHeight=0); ~MyOVERLAPPED(); }; // 預設建構函式 MyOVERLAPPED::MyOVERLAPPED() :pBuff(nullptr) { OVERLAPPED::hEvent = 0; } // 帶參建構函式 MyOVERLAPPED::MyOVERLAPPED(int nIoSize, int nFileOffsetLow, int
nFileOffsetHeight) { // 建立事件物件 OVERLAPPED::hEvent = CreateEvent(NULL,FALSE,FALSE,NULL); // 儲存檔案讀寫偏移 OVERLAPPED::Offset = nFileOffsetLow; OVERLAPPED::OffsetHigh = nFileOffsetHeight; // 申請緩衝區儲存檔案內容 pBuff = new char[nIoSize]; } // 解構函式,釋放空間,釋放事件物件 MyOVERLAPPED::~MyOVERLAPPED() { // 釋放空間 if (pBuff != nullptr) { delete[] pBuff; pBuff = nullptr; } // 關閉事件物件 if (hEvent != NULL) { CloseHandle(hEvent); } } // 等待IO事件有訊號的函式 unsigned int __stdcall IOProc(void *pArg) { MyOVERLAPPED * pMyOverlapped = (MyOVERLAPPED*)pArg; WaitForSingleObject(pMyOverlapped->hEvent, INFINITE); printf("[%d] IO任務完成,讀取位元組:%d,讀取位置:%d,讀取內容:[%s]\n}", pMyOverlapped->nIndex, pMyOverlapped->InternalHigh, pMyOverlapped->Offset, pMyOverlapped->pBuff); // 釋放空間 delete pMyOverlapped; return 0; } 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, // 使用非同步IO NULL); if (hFile == INVALID_HANDLE_VALUE) return 0; int nSize = GetFileSize(hFile,NULL); // 配置非同步IO的資訊 // 因為要傳遞到執行緒的函式中使用,所以,在這裡必須要儲存到堆空間中 // 如果是棧空間,則出了當前這個函式時,棧空間就會被釋放 MyOVERLAPPED *ov = new MyOVERLAPPED; ov->Offset = 0; // 要讀取的檔案偏移 ov->OffsetHigh = 0; ov->hEvent = CreateEvent(NULL,FALSE,FALSE,NULL); ov->pBuff = new char[nSize]{}; ov->nIndex = 0; // 序號 // 派發IO任務 ReadFile(hFile, ov->pBuff,nSize,NULL,ov); // 派發第二個IO任務 - 配置IO資訊 // 下面程式碼的寫法比上面的寫法精簡了很多,但是功能完全一樣 int nFileRead = 50; MyOVERLAPPED *ov2 = new MyOVERLAPPED(nFileRead,300); ov2->nIndex = 1; // 序號 ReadFile(hFile, ov2->pBuff, nFileRead, NULL, ov2); // 建立執行緒等到IO任務全部完成 HANDLE hThreadEvent[2] = {}; hThreadEvent[0] = (HANDLE)_beginthreadex(0, 0, IOProc, ov, 0, 0); hThreadEvent[1] = (HANDLE)_beginthreadex(0, 0, IOProc, ov2, 0, 0); WaitForMultipleObjects(_countof(hThreadEvent), hThreadEvent, TRUE, -1); system("pause"); return 0; }