I/O 完成埠
I/O 完成埠提供有效率的線程模型,可在多處理器系統上處理多個異步 I/O 要求。 當進程建立 I/O 完成埠時,系統會為唯一目的是為服務這些要求的線程建立相關聯的佇列物件。 處理許多並行異步 I/O 要求的進程可以透過搭配預先配置的線程集區使用 I/O 完成埠,比在收到 I/O 要求時建立線程更快速且有效率地執行此動作。
I/O 完成埠的運作方式
CreateIoCompletionPort 函式會建立 I/O 完成埠,並將一或多個檔句柄與該埠產生關聯。 當其中一個檔案句柄上的異步 I/O 作業完成時,I/O 完成封包會排入先出 (FIFO) 順序,以相關聯的 I/O 完成埠。 此機制的其中一個強大用途是將多個檔句柄的同步處理點合併成單一物件,但也有其他有用的應用程式。 請注意,當封包依 FIFO 順序排入佇列時,封包可能會以不同的順序取消排入佇列。
注意
這裡所使用的 檔句柄 一詞是指代表重疊 I/O 端點的系統抽象概念,而不只是磁碟上的檔案。 例如,它可以是網路端點、TCP 套接字、命名管道或郵件位置。 任何支援重疊 I/O 的系統物件都可以使用。 如需相關 I/O 函式的清單,請參閱本主題的結尾。
當檔句柄與完成埠相關聯時,在封包從完成埠移除之前,傳入的狀態區塊將不會更新。 唯一的例外狀況是原始作業以同步方式傳回錯誤。 線程(主線程或主線程本身所建立的線程)會使用 GetQueuedCompletionStatus 函式來等候完成封包排入佇列至 I/O 完成埠,而不是直接等候異步 I/O 完成。 封鎖其在 I/O 完成埠上執行的線程會以最後先出 (LIFO) 順序發行,而下一個完成封包會從該線程的 I/O 完成埠 FIFO 佇列提取。 這表示,當完成封包釋放至線程時,系統會釋放與該埠相關聯的最後一個(最近)線程,並傳遞最舊 I/O 完成的完成資訊。
雖然任意數目的線程可以針對指定的 I/O 完成埠呼叫 GetQueuedCompletionStatus,但當指定的線程第一次 呼叫 getQueuedCompletionStatus 時,它會與指定的 I/O 完成埠相關聯,直到發生下列三件事之一:線程結束,指定不同的 I/O 完成埠, 或關閉 I/O 完成埠。 換句話說,單一線程最多可以與一個 I/O 完成埠相關聯。
當完成封包排入 I/O 完成埠佇列時,系統會先檢查與該埠相關聯的線程數目正在執行。 如果執行的線程數目小於並行值(在下一節中討論),則允許其中一個等候的線程(最新的線程)處理完成封包。 當執行中的線程完成其處理時,通常會再次呼叫 GetQueuedCompletionStatus,此時它會以下一個完成封包傳回,或等候佇列是空的。
線程可以使用 PostQueuedCompletionStatus 函式,將完成封包放在 I/O 完成埠的佇列中。 如此一來,除了從 I/O 系統接收 I/O 完成封包之外,完成埠還可用來接收來自進程其他線程的通訊。 PostQueuedCompletionStatus 函式可讓應用程式將自己的特殊用途完成封包排入 I/O 完成埠,而不需要啟動異步 I/O 作業。 例如,這適用於通知背景工作線程外部事件。
I/O 完成連接埠句柄和與該特定 I/O 完成埠相關聯的每個檔案句柄稱為 I/O 完成埠的參考。 當不再參考 I/O 完成埠時,即會釋出該埠。 因此,所有這些句柄都必須正確關閉,才能釋放 I/O 完成埠及其相關聯的系統資源。 滿足這些條件之後,應用程式應該藉由呼叫 CloseHandle 函式來關閉 I/O 完成埠句柄。
注意
I/O 完成埠會與建立它的進程相關聯,而且無法在進程之間共用。 不過,同一個進程中的線程之間可以共用單一句柄。
線程和並行
要仔細考慮之 I/O 完成埠的最重要屬性是並行值。 透過 NumberOfConcurrentThreads 參數,使用 createIoCompletionPort 建立完成埠的並行值時,會指定它。 此值會限制與完成埠相關聯的可執行線程數目。 當與完成埠相關聯的可執行線程總數達到並行值時,系統會封鎖與該完成埠相關聯的任何後續線程執行,直到可執行線程數目低於並行值為止。
有完成封包在佇列中等候,但無法滿足等候,因為埠已達到其並行限制,因此會發生最有效率的情況。 請考慮在 getQueuedCompletionStatus 函數調用中等候的一個和多個線程並行值會發生什麼事。 在此情況下,如果佇列一律有完成封包等候,當執行中的線程呼叫 GetQueuedCompletionStatus時,它不會封鎖執行,因為如先前所述,線程隊列為 LIFO。 相反地,此線程會立即挑選下一個佇列完成封包。 不會發生線程內容切換,因為執行中的線程會持續擷取完成封包,而其他線程則無法執行。
注意
在上一個範例中,額外的線程似乎無用且永不執行,但假設執行中的線程永遠不會被其他機制置於等候狀態、終止,或關閉其相關聯的 I/O 完成埠。 在設計應用程式時,請考慮所有這類線程執行影響。
要為並行值挑選的最佳整體最大值是電腦上的 CPU 數目。 如果您的交易需要較長的計算,較大的並行值將允許更多線程執行。 每個完成封包可能需要較長的時間才能完成,但將會同時處理更多完成封包。 您可以搭配分析工具來實驗並行值,以達到應用程式的最佳效果。
如果與相同 I/O 完成埠相關聯的另一個執行線程因其他原因而進入等候狀態,GetQueuedCompletionStatus 處理完成封包,例如,SuspendThread 函式。 當處於等候狀態的線程再次開始執行時,使用中線程的數目可能會短暫超過並行值。 不過,在使用中線程數目低於並行值之前,系統不會允許任何新的使用中線程,以快速減少此數目。 這是讓應用程式在其線程集區中建立比並行值更多的線程的原因之一。 線程集區管理超出本主題的範圍,但良好的經驗法則是線程集區中至少有兩倍的線程,因為系統上有處理器。 如需線程共用的詳細資訊,請參閱 線程集區。
支援的 I/O 函式
下列函式可用來啟動使用 I/O 完成埠完成的 I/O 作業。 您必須將函式傳遞 重疊 結構的實例,以及先前與 I/O 完成埠相關聯的檔句柄(透過呼叫 CreateIoCompletionPort),才能啟用 I/O 完成埠機制:
- AcceptEx
- ConnectNamedPipe
- DeviceIoControl
- LockFileEx
- ReadDirectoryChangesW
- ReadFile
- TransactNamedPipe
- WaitCommEvent
- WriteFile
- WSASendMsg
- WSASendTo
- WSASend
- WSARecvFrom
- LPFN_WSARECVMSG (WSARecvMsg)
- WSARecv
相關主題
-
關於進程和線程 的