多线程笔记5
第六章:Overlapped I/O,在你身后變戲法
1.overlapped I/O 是 Win32 的一項(xiàng)技術(shù),你可以要求操作系統(tǒng)為你傳送數(shù)據(jù),并且在傳送完畢時通知你。這項(xiàng)技術(shù)使你的程序在I/O 進(jìn)行過程中仍然能夠繼續(xù)處理事務(wù)。事實(shí)上,操作系統(tǒng)內(nèi)部正是以線程來完成 overlapped I/O。
2.Win32文件操作函數(shù)
(1)CreateFile()可以用來打開文件、串行口和并行口、Named pipes、Console。
HANDLE CreateFile(
LPCTSTR lpFileName, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 指向文件名稱
DWORD dwDesiredAccess, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 存取模式(讀或?qū)?#xff09;
DWORD dwShareMode, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 共享模式(share mode)
LPSECURITY_ATTRIBUTES lpSecurityAttributes, ?// 指向安全屬性結(jié)構(gòu)
DWORD dwCreationDisposition, ? ? ? ? ? ? ? ? ? ? ? ? ?// 如何產(chǎn)生
DWORD dwFlagsAndAttributes, ? ? ? ? ? ? ? ? ? ? ? ? ?// 文件屬性
HANDLE hTemplateFile ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 一個臨時文件,將擁有全部的屬性拷貝
);
注:overlapped I/O性質(zhì):可以在同一時間讀寫文件的許多部分;多個overlapped請求,執(zhí)行次序無法保證;overlapped I/O的基本型式是以ReadFile()和WriteFile()完成的。
(2)ReadFile()
BOOL ReadFile(
HANDLE hFile, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 欲讀之文件
LPVOID lpBuffer, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 接收數(shù)據(jù)之緩沖區(qū)
DWORD nNumberOfBytesToRead, ? ? ? ? // 欲讀取的字節(jié)個數(shù)
LPDWORD lpNumberOfBytesRead, ? ? ? ?// 實(shí)際讀取的字節(jié)個數(shù)的地址
LPOVERLAPPED lpOverlapped ? ? ? ? ? ? ? // 指針,指向 overlapped info
);
(3)WriteFile()
BOOL WriteFile(
HANDLE hFile, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 欲寫之文件
LPCVOID lpBuffer, ? ? ? ? ? ? ? ? ? ? ? ? ? ? // 儲存數(shù)據(jù)之緩沖區(qū)
DWORD nNumberOfBytesToWrite, ? ? ? // 欲寫入的字節(jié)個數(shù)
LPDWORD lpNumberOfBytesWritten, ? // 實(shí)際寫入的字節(jié)個數(shù)的地址
LPOVERLAPPED lpOverlapped ? ? ? ? ? ? // 指針,指向 overlapped info
);
如果CreateFile() 的第6個參數(shù)被指定為FILE_FLAG_ OVERLAPPED,就必須在上述的 lpOverlapped 參數(shù)中提供一個指針,指向一個 OVERLAPPED 結(jié)構(gòu)。
(4)OVERLAPPED結(jié)構(gòu)
typedef struct _OVERLAPPED {
DWORD Internal; ? ? ? ?//通常保留。當(dāng)GetOverlappedResult傳回False,并且GetLastError并非傳回ERROR_IO_PENDING,則內(nèi)含一個視系統(tǒng)而定的狀態(tài)。
DWORD InternalHigh; //通常被保留,當(dāng)GetOverlappedResult傳回True,則內(nèi)含“被傳輸數(shù)據(jù)的長度”
DWORD Offset; ? ? ? ? ?//讀、寫偏移位置,從文件頭開始算起。若目標(biāo)設(shè)備不支持文件位置,忽略
DWORD OffsetHigh; ? //64位文件偏移中較高32位。若目標(biāo)設(shè)備不支持文件位置,忽略
HANDLE hEvent; ? ? ? //manual reset event,overlapped I/O完成時被激發(fā)。ReadFileEX,WriteFileEX忽略這個欄位,彼時被用來傳遞一個用戶自定義的指針。
} OVERLAPPED, *LPOVERLAPPED;
OVERLAPPED 結(jié)構(gòu)執(zhí)行兩個重要的功能。第一,它像一把鑰匙,用以識別每一個目前正在進(jìn)行的 overlapped 操作。第二,它在你和系統(tǒng)之間提供了一個共享區(qū)域,參數(shù)可以在該區(qū)域中雙向傳遞。
通常overlapped結(jié)構(gòu)存放在heap中。
3.被激發(fā)的File Handles
(1)異步IO的步驟:CreateFile指定FILE_FLAG_OVERLAPPED;設(shè)立一個OVERLAPPED結(jié)構(gòu),調(diào)用ReadFile、WriteFile帶上這個參數(shù)。
(2)文件handle是一個核心對象,一旦操作完畢即被激發(fā)。
(3)GetOverlappedResult()
BOOL GetOverlappedResult(
HANDLE hFile, ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//文件設(shè)備的handle
LPOVERLAPPED lpOverlapped, ? ? ? ? ? ? ? ? //一個指針,指向overlapped結(jié)構(gòu)
LPDWORD lpNumberOfBytesTransferred, ?//一個指針,指向DWORD,保存真正被傳輸?shù)淖止?jié)數(shù)。
BOOL bWait ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//是否要等待操作完成,TRUE表示等待。
);
(4)雖然你要求一個overlapped 操作,但它并不一定就是 overlapped!如果數(shù)據(jù)已經(jīng)被放進(jìn) cache中,或如果操作系統(tǒng)認(rèn)為它可以很快速地取得那份數(shù)據(jù),那么文件操作就會在ReadFile() 返回之前完成,而 ReadFile() 將傳回 TRUE。
(5)一個文件操作為 overlapped,而操作系統(tǒng)把“操作請求”放到隊(duì)列中等待執(zhí)行, ReadFile() 和、WriteFile()都會傳回 FALSE 以示失敗。這個行為并不是很直觀, 你必須調(diào)用GetLastError() 并確定它傳回 ERROR_IO_PENDING,那意味著“overlappedI/O 請求”被放進(jìn)隊(duì)列之中等待執(zhí)行。GetLastError() 也可能傳回其他的值,例如 ERROR_HANDLE_EOF,那就真正代表一個錯誤了。
4.被激發(fā)的event對象
(1)所使用的 event 對象必須是手動重置(manual-reset)而非自動重置(auto-reset)。
(2)IOBYEVENT例子。
5.異步過程調(diào)用(Asynchronous Procedure Calls,APCs)
(1)使用overlapped I/O與event搭配的兩個問題:
<1>WaitForMultipleObjects最多等待64個對象。
<2>必須不斷的根據(jù)“哪一個handle被激發(fā)”而計(jì)算如何反應(yīng)。
(2)使用Ex版的ReadFile和WriteFile,可以使用異步過程調(diào)用機(jī)制。只有當(dāng)線程處于alertable狀態(tài)時,APCs才會被調(diào)用。當(dāng)線程因?yàn)橐韵?個函數(shù)而處于等待狀態(tài),且線程的“alertable”標(biāo)記被設(shè)為TRUE,則線程處于alertable狀態(tài):
SleepEx()
WaitForSingleObjectEx()
WaitForMultipleObjectEx()
MsgWaitForMultipleObjectsEx()
SignalObjectAndWait();
(3)用于 overlapped I/O 的 APCs 是一種所謂的 user mode APCs。WindowsNT 另有一種所謂的 kernel mode APCs。Kernel mode APCs 也會像 usermode APCs 一樣被保存起來,但一個 kernel mode APC 一定會在下一個timeslice 被調(diào)用,不管線程當(dāng)時正在做什么。 Kernel mode APCs 用來處理系統(tǒng)機(jī)能,不在應(yīng)用程序的控制之中。
(4)提供的 I/O completion routine 應(yīng)該有這樣的型式:
VOID WINAPI FileIOCompletionRoutine(
DWORD dwErrorCode, ? //0表示操作完成,ERROR_HANDLE_EOF表示操作已經(jīng)到了文件尾端。
DWORD dwNumberOfBytesTransferred,//真正被傳輸?shù)臄?shù)據(jù)字節(jié)數(shù)
LPOVERLAPPED lpOverlapped//指向overlapped結(jié)構(gòu),此結(jié)構(gòu)由開啟overlapped I/O操作的函數(shù)提供
);
(5)使用 APCs 時,OVERLAPPED 結(jié)構(gòu)中的 hEvent 欄位不需要用來放置一個 event handle。Win32 文件上說此時 hEvent 欄位可以由程序員自由運(yùn)用。那么最大的用途就是:首先配置一個結(jié)構(gòu),描述數(shù)據(jù)來自哪里,或是要對數(shù)據(jù)進(jìn)行一些什么操作,然后將 hEvent 欄位設(shè)定指向該結(jié)構(gòu)
(6)在C++ 中產(chǎn)生一個I/O Completion Routines:儲存一個指針,指向用戶自定義數(shù)據(jù)(一個對象),然后經(jīng)由此指針調(diào)用一個 C++ 成員函數(shù)。由于 static 成員函數(shù)是類的一部分,你還是可以調(diào)用 private 成員函數(shù)。
6.對文件進(jìn)行overlapped I/O的缺點(diǎn)
(1)似乎 Windows NT 是以“I/O 請求”的大小來決定要不要將此請求先記錄下來。所以對于數(shù)據(jù)量小的操作,overlapped I/O的效率反而更低。
(2)解決辦法:以少量的線程負(fù)責(zé)所有的硬盤 I/O,然后把這些線程的I/O 請求,保持在一個隊(duì)列之中。這種效率比較高。
(3)有兩種情況,overlapped I/O 總是同步執(zhí)行,甚至即使 FILE_FLAG_NO_BUFFERING 已經(jīng)指定。第一種情況是你進(jìn)行一個寫入操作而造成文件的擴(kuò)展。第二種情況是你讀寫一個壓縮文件。
7.I/O Completion Ports
(1)APCs的缺點(diǎn):最大的問題就是,有好幾個 I/O APIs 并不支持 APCs,如listen() 和 WaitCommEvent() 便是兩個例子。APCs 的另一個問題是,只有發(fā)出“overlapped 請求”的那個線程才能夠提供 callback 函數(shù),然而在一個“scalable”(譯注)系統(tǒng)中,最好任何線程都能夠服務(wù) events。
(2)產(chǎn)生一個I/O Completion Port
HANDLE CreateIoCompletionPort(
HANDLE FileHandle, ? ? ? ? ? ? ? ? ? ? ? ?//文件或設(shè)備的handle,若為INVALID_HANDLE_VALUE,則產(chǎn)生一個沒有和任何handle關(guān)聯(lián)的port。
HANDLE ExistingCompletionPort, ? ? ?//若此欄位被指定,則FileHandle被加到此port上。指定Null產(chǎn)生一個新的port。
DWORD CompletionKey, ? ? ? ? ? ? ? ? ?//用戶自定義的一個數(shù)值,將被交給提供服務(wù)的線程。此值和FileHanlde有關(guān)聯(lián)。
DWORD NumberOfConcurrentThreads//與此I/O completion port 有關(guān)聯(lián)的線程個數(shù)。
);
(3)與一個文件handle產(chǎn)生關(guān)聯(lián)
再次使用CreateIoCompletionPort接口。
(4)在一個I/O Completion Port上等待
BOOL GetQueuedCompletionStatus(
HANDLE CompletionPort, ? ? ? ? ? ? ? ? ? ? ? ?//將在其上等待completion port。
LPDWORD lpNumberOfBytesTransferred, ?//指向DWORD,收到“被傳輸?shù)臄?shù)據(jù)字節(jié)數(shù)”。
LPDWORD lpCompletionKey, ? ? ? ? ? ? ? ? ? //指向DWORD,該DWORD將收到由CreateIoCompletionPort定義的key。
LPOVERLAPPED *lpOverlapped, ? ? ? ? ? ? ? //overlapped結(jié)構(gòu)指針的地址。
DWORD dwMilliseconds ? ? ? ? ? ? ? ? ? ? ? ? ?//等待的最長時間,時間終了,lpOverlapped被設(shè)為NULL,函數(shù)傳回FALSE。
);
在completion port上等待的線程是以先進(jìn)后出的次序提供服務(wù)。
(5)避免Completion Packets
設(shè)定一個 OVERLAPPED 結(jié)構(gòu),內(nèi)含一個合法的手動重置(manual-reset)event 對象,放在 hEvent 欄位。然后把該 handle 的最低位設(shè)為 1。
overlap.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
overlap.hEvent = (HANDLE)((DWORD)overlap.hEvent | 0x1);
WriteFile(hFile, buffer, 128,& dwBytesWritten, &overlap);
8.對Sockets使用Overlapped I/O
分析ECHO例子,多實(shí)踐socket IOCP。
轉(zhuǎn)載于:https://www.cnblogs.com/programmer-wfq/p/4646151.html
總結(jié)
- 上一篇: 莫队算法 BOJ 2038 [2009国
- 下一篇: ASP.NET中禁止继承IIS中web.