http://www.cnblogs.com/skyofbitbit/p/3650140.html
I/O裝置處理必然讓主程式停下來乾等I/O的完成,
對這個問題有
方法一:使用另一個線程進行I/O。這個方案可行,但是麻煩。 即 CreateThread(…………);建立一個子線程做其他事情。 Readfile(^…………);阻塞方式讀資料。
方法二:使用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用於記錄了當前正在操作的檔案一些相關資訊。
//功能:從指定檔案的1500位置讀入300個位元組
int main()
{
BOOL rc;
HANDLE hFile;
DWORD numread;
OVERLAPPED overlap;
char buf[512];
char szPath=”c:\\xxxx\xxxx”;
hFile = CreateFile( szPath,
GENERIC_READ,
FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED, // 以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結構中提供的event來解決上面遇到的問題。
注意,你所使用的event對象必須是一個MANUAL型的;否則,可能產生競爭條件。
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++) requests 同時有N個同時讀取檔案
{
//將同一檔案按幾個部分按overlapped方式同時讀
//注意看QueueRequest函數是如何運做的,每次讀16384個塊
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;
}
程式流程:
1: N個使用者同時讀取一個檔案的各個部分,且每個使用者對應一個重疊對象和事件。
2:調用WaitForMultipleObjects(MAX_REQUESTS, ghEvents, TRUE, INFINITE) 當任何一個使用者的讀操作完成時,函數停止阻塞。並且ghEvents中對應於的讀取資料完畢的使用者的事件被啟用。
3:調用GetOverlappedResult 取得讀取資料完畢的使用者編號。