l 建立完成連接埠
完成連接埠的原理是並發啟動並執行線程必須有一個上限;也就是500個並行的用戶端不允許500個線程同時存在。那麼,僅僅是確切的並發啟動並執行線程數目嗎?是的,如果仔細想想,就會意識到如果在一個雙CPU的機器上有多於兩個可啟動並執行線程,而每個CPU對應於一個線程,並沒有多大意義。一旦線程數量超過可用的CPU數量,系統就不得不花費時間進行線程切換,這將浪費CPU周期,這是並行模型的低效之處。
並行模型的另一個低效之處是為每個用戶端請求建立新線程。相對於在本身的虛地址空間建立新進程來說,建立線程代價較低,但並不是免費的。如果在服務應用程式初始化的時候建立線程池將會提高效能,這些線程可以在程式內迴圈使用。I/O完成連接埠被設計成使用線程池來工作。
I/O完成連接埠可能是最複雜的核心對象。要建立I/O完成連接埠,需要調用CreateIoCompletionPort函數:
HANDLE CreateIoCompletionPort(
HANDLE hfile,
HANDLE hExistingCompPort,
ULONG_PTR CompKey,
DWORD dwNumberOfConcurrentThreads);
該函數執行兩個不同的任務:建立I/O完成連接埠,將裝置與I/O完成連接埠關聯。這個函數相當複雜,在我看來,微軟應該將其拆分為兩個函數。當我使用I/O完成連接埠時,建立兩個小函數來將CreateIoCompletionPort的兩種能力分開。我建立第一個函數稱為CreateNewCompletionPort,實現如下:
HANDLE CreateNewCompletionPort(DWORD dwNumberOfConcurrentThreads) {
return(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0,
dwNumberOfConcurrentThreads));
}
該函數帶有一個參數:dwNumberOfConcurrentThreads,內部調用CreateIoCompletionPort函數,將硬式編碼值作為前三個參數,dwNumberOfCocurrentThreads作為最後一個。可以看到,CreateIoCompletionPort函數的前三個參數僅在將裝置與完成連接埠關聯時有用(很快會講到)。為了建立完成連接埠,我將用INVALID_HANDLE_VALUE,NULL,0來對應CreateIoCompletionPort的前三個參數。
dwNumberOfConcurrentThreads參數告訴完成連接埠同時可運行線程的最大數量。如果該參數為0,完成連接埠預設與值與機器的CPU數相同。這對於避免額外的線程切換相當有用。如果用戶端請求需要較長的運算,增加這個值會減少阻塞,但強烈建議不要增加這個值。可以嘗試不同的值來體驗一下應用程式的表現。