我們知道當程式調用I/O裝置處理一些事情時,讓主程式停下來乾等I/O的完成是沒有效率的。對這個問題有下面幾種解決方案:
方法一:使用另一個線程進行I/O。但從以前的學習中也知道,協調線程間的關係是一件麻煩的事情,需要小心的設計;所以這個方案可行,但是麻煩。
方法二:使用overlapped I/O。正如書上所說:“overlapped I/O是WIN32的一項技術,你可以要求作業系統為你傳送資料,並且在傳送完畢時通知你。這項技術使你的程式在I/O進行過程中仍然能夠繼續處理事務。事實上,作業系統內部正是以線程來I/O完成overlapped I/O。你可以獲得線程的所有利益,而不需付出什麼痛苦的代價”。哈!這真是一個好方法!!! 下面的內容就是來談怎樣使用overlapped I/O:
進行I/O操作時,指定overlapped方式使用CreateFile(),將其第6個參數指定為FILE_FLAG_OVERLAPPED,就是準備使用overlapped的方式構造或開啟檔案;如果採用overlapped,那麼ReadFile()、WriteFile()的第5個參數必須提供一個指標,指向一個OVERLAPPED結構。OVERLAPPED用於記錄了當前正在操作的檔案一些相關資訊。
//下面引用書上的例子,來說明overlapped:
//功能:從指定檔案的1500位置讀入300個位元組
int main()
{
BOOL rc;
HANDLE hFile;
DWORD numread;
OVERLAPPED overlap;
char buf[512];
char szPath=”x://xxxx/xxxx”;
//檢查系統,確定是否支援overlapped,(NT以上作業系統支援OVERLAPPED)
CheckOsVersion();
// 以overlapped的方式開啟檔案
hFile = CreateFile( szPath,
GENERIC_READ,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED
,
NULL
);
// OVERLAPPED結構實始化為0
memset(&overlap, 0, sizeof(overlap));
//指定檔案位置是1500;
overlap.Offset = 1500;
rc =
ReadFile
(hFile,buf,300,&numread,&overlap);
//因為是overlapped操作,ReadFile會將讀檔案請求放入讀隊列之後立即返回(false),
//而不會等到檔案讀完才返回(true)
if (rc)
{
//檔案真是被讀完了,rc為true
// 或當資料被放入cache中,或作業系統認為它可以很快速地取得資料,rc為true
}
else
{
if (GetLastError() == ERROR_IO_PENDING)
{
//當錯誤是ERROR_IO_PENDING,那意味著讀檔案的操作還在進行中
//等候,直到檔案讀完
WaitForSingleObject(hFile, INFINITE);
rc = GetOverlappedResult(hFile,&overlap,&numread,
FALSE
);
//上面二條陳述式完成的功能與下面一條語句的功能等價:
// GetOverlappedResult(hFile,&overlap,&numread,
TRUE
);
}
else
{
//出錯了
}
}
CloseHandle(hFile);
return EXIT_SUCCESS;
}
讀過上面的程式,對overlapped就有一個大概的印象;接著我們繼續探索overlapped I/O的強大功能。
在實際工作中,可能會有多個操作同時使用
同一個
檔案handle,那麼上面的程式就不再適用了。怎麼辦?我們可以利用OVERLAPPED結構中提供的event來解決上面遇到的問題。注意,你所使用的event對象必須是一個MANUAL型的;否則,可能產生競爭條件。原因見書P159。
//程式片段:
int main()
{
int i;
BOOL rc;
char szPath=”x://xxxx/xxxx”;
// 以overlapped的方式開啟檔案
ghFile = CreateFile( szPath,
GENERIC_READ,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
NULL
);
for (i=0; i<MAX_REQUESTS; i++)
{
//將同一檔案按幾個部分按overlapped方式同時讀
//注意看QueueRequest函數是如何運做的
QueueRequest(i, i*16384, READ_SIZE);
}
// 等候所有操作結束;
//隱含條件:當一個操作完成時,其對應的event對象會被啟用
WaitForMultipleObjects(
MAX_REQUESTS,
ghEvents
, TRUE, INFINITE
);
// 收尾操作
for (i=0; i<MAX_REQUESTS; i++)
{
DWORD dwNumread;
rc = GetOverlappedResult(
ghFile,
&gOverlapped[i],
&dwNumread,
FALSE
);
CloseHandle(gOverlapped[i].hEvent);
}
CloseHandle(ghFile);
return EXIT_SUCCESS;
}
//當讀操作完成以後,
gOverlapped[nIndex].hEvent
會系統被激發
int QueueRequest(int nIndex, DWORD dwLocation, DWORD dwAmount)
{
//構造一個MANUAL型的event對象
ghEvents[nIndex] = CreateEvent(NULL, TRUE, FALSE, NULL);
//將此event對象置入OVERLAPPED結構
gOverlapped[nIndex].hEvent = ghEvents[nIndex];
gOverlapped[nIndex].Offset = dwLocation;
for (i=0; i<MAX_TRY_COUNT; i++)
{
//檔案ghFile唯一
rc = ReadFile(
ghFile
, gBuffers[nIndex],&dwNumread,&gOverlapped[nIndex]);
//處理成功
if (rc) return TRUE;
err = GetLastError();
if (err == ERROR_IO_PENDING)
{
//當錯誤是ERROR_IO_PENDING,那意味著讀檔案的操作還在進行中
return TRUE;
}
// 處理一些可恢複的錯誤
if ( err == ERROR_INVALID_USER_BUFFER ||
err == ERROR_NOT_ENOUGH_QUOTA ||
err == ERROR_NOT_ENOUGH_MEMORY )
{
Sleep(50);
continue;
//重試
}
// 如果GetLastError()返回的不是以上列出的錯誤,放棄
break;
}
return -1;
} 嗨!當大家耐著性子看到這裡肯定都在罵,這寫的什麼東西,照本宣科嘛。唉,沒辦法,這個東西不好舉例子,只有寫兩個程式片段,加強記憶了,見諒、見諒!!!