【Windows原理】IO非同步-等待事件物件
阿新 • • 發佈:2018-11-08
/*
同步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;
}