PostQueuedCompletionStatus函數,向每個工作者線程都發送—個特殊的完成資料包。該函數會指示每個線程都“立即結束並退出”.下面是PostQueuedCompletionStatus函數的定義:
BOOL PostQueuedCompletionStatus(
HANDLE CompletlonPort,
DW0RD dwNumberOfBytesTrlansferred,
DWORD dwCompletlonKey,
LPOVERLAPPED lpoverlapped,
);
其中,CompletionPort參數指定想向其發送一個完成資料包的完成連接埠對象。而就dwNumberOfBytesTransferred,dwCompletionKey和lpOverlapped這三個參數來說.每—個都允許我們指定—個值,直接傳遞給GetQueuedCompletionStatus函數中對應的參數。這樣—來。—個工作者線程收到傳遞過來的三個GetQueuedCompletionStatus函數參數後,便可根據由這三個參數的某一個設定的特殊值,決定何時應該退出。例如,可用dwCompletionPort參數傳遞0值,而—個工作者線程會將其解釋成中止指令。一旦所有工作者線程都已關閉,便可使用CloseHandle函數,關閉完成連接埠。最終安全退出程式。
PostQueuedCompletionStatus函數提供了一種方式來與線程池中的所有線程進行通訊。如,當使用者終止服務應用程式時,我們想要所有線程都完全利索地退出。但是如果各線程還在等待完成連接埠而又沒有已完成的I/O 請求,那麼它們將無法被喚醒。
通過為線程池中的每個線程都調用一次PostQueuedCompletionStatus,我們可以將它們都喚醒。每個線程會對GetQueuedCompletionStatus的傳回值進行檢查,如果發現應用程式正在終止,那麼它們就可以進行清理工作並正常地退出。
GetQueuedCompletionStatus函數有個OVERLAPPED結構,很多資料上都採用不同的結構體來擴充該結構,比如有的資料定義:
typedef struct _OVERLAPPEDPLUS
{
OVERLAPPED ol;
SOCKET s, sclient;
int OpCode;
WSABUF wbuf;
DWORD dwBytes, dwFlags;
}OVERLAPPEDPLUS;
然後,當GetQueuedCompletionStatus(hIocp, &dwBytesXfered,(PULONG_PTR)&PerHandleKey, &Overlap, INFINITE);函數返回時候,人們常用OverlapPlus = CONTAINING_RECORD(Overlap, OVERLAPPEDPLUS, ol)得到一些資訊。比如此時連接埠上完成的是什麼操作,資料是什麼等,還有,系統如何做到自動填滿上述的結構的,也就是說,系統怎麼知道在Overlap->OpCode存放的應該是操作類型,如讀,寫操作,而在Overlap->wbuf存放的應該是讀寫資料。
Overlap->OpCode,操作類型是在投遞WSASend,WSARecv的時候,由你自己指定填充這個欄位。
因為是非堵塞的,等於投遞到與通訊端相關聯的完成連接埠上,系統會把把WSASend對應的緩衝區提交到底層緩衝,也可以把WSARecv投遞的緩衝區,用接收到的資料填充,每一個WSASend,WSARecv,都應有新申請一個overlaspped plus結構提交,以存放本次投遞的IO操作的相關資料,——單IO操作資料所以工作器線程中,從完成連接埠隊列中get得到一個完成包的時候,可以根據單控制代碼資料知道在這個完成連接埠上是哪一個通訊端投遞的IO操作完成了,從get到的overlapped中得到相關的已經完成IO資料和資訊,並作相應的處理。比如投遞了1M,完成包卻告知只完成512K,那麼你就知道要把餘下的512K繼續投遞WSASend,當然上一個WSASend的Overlapped這個時候可以重用到下一個WSASend中,這個是允許的,可以用一個欄位存放全部1M,把餘下未Send成功512k放到wbuf中,繼續投遞或者投遞WSARecv1M資料,卻收到一個512K的完成通知,那麼你要繼續投遞WSARecv,當然前一個WSARecv的overlapped也可以重用,不過需要一些處理,把已經接收到的512K儲存到某個欄位中,再投遞一個512K的請求去接收完成連接埠內部,對投遞的Overlapped的填充,好像只有WSARecv的時候填充WSABUF,其他都是投遞IO前,代碼中顯式填充,並投遞的。至於完成了多少個位元組,是在lpNumberOfBytes中得到。
對GetQueuedCompletionStatus函數解釋:
實現從指定的IOCP擷取CP。當CP隊列為空白時,對此函數的調用將被阻塞,而不是一直等待I/O的完成。當CP隊列不為空白時,被阻塞的線程將以後進先出(LIFO)順序被釋放。對於IOCP機制,它允許多線程並發調用GetQueuedCompletionStatus函數,最大並發數是在調用CreateIoCompletionPort函數時指定的,超出最大並發數的調用線程,將被阻塞。函數解釋如下:
聲明:
BOOL GetQueuedCompletionStatus(
HANDLE CompletionPort,
LPDWORD lpNumberOfBytes,
PULONG_PTR lpCompletionKey,
LPOVERLAPPED *lpOverlapped,
DWORD dwMilliseconds);
調用參數:
CompletionPort:指定的IOCP,該值由CreateIoCompletionPort函數建立。
lpnumberofbytes:一次完成後的I/O操作所傳送資料的位元組數。
lpcompletionkey:當檔案I/O操作完成後,用於存放與之關聯的CK。
lpoverlapped:為調用IOCP機制所引用的OVERLAPPED結構。
dwmilliseconds:用於指定調用者等待CP的時間。
傳回值:
調用成功,則返回非零數值,相關資料存於lpNumberOfBytes、lpCompletionKey、lpoverlapped變數中。失敗則返回零值。
#define CONTAINING_RECORD(address, type, field) ((type *)( /
(PCHAR)(address) - /
(ULONG_PTR)(&((type *)0)->field)))
用於取得記憶體中任何結構體的首地址,要提供的參數是:結構體中某個成員(field)的地址address、結構體的類型type、提供地址那個成員的名字field。(ULONG_PTR)(&((type *)0)->field)),它表示結構體type的field欄位相對type首地址的記憶體位址位移量,也可以如此理解:結構體首地址從0開始數,field的位移就是(ULONG_PTR)(&((type *)0)->field))type結構體的field欄位的地址減去field欄位在結構體中的位移,就是結構體的首地址。
如:
typedef struct tagSP_IocpEvent {
enum { SP_IOCP_MAX_IOV = 8 };
enum { eEventUnknown, eEventRecv, eEventSend, eEventTimer };
OVERLAPPED mOverlapped;
int mType;
WSABUF mWsaBuf;
void ( * mOnTimer ) ( void * );
int mHeapIndex;
struct timeval mTimeout;
} SP_IocpEvent_t;
SP_IocpEvent_t * iocpEvent = CONTAINING_RECORD( overlapped, SP_IocpEvent_t, mOverlapped );