【Windows原理】非同步IO-_APC(非同步過程呼叫)
阿新 • • 發佈:2018-11-08
// 同步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;
}
}