完成埠(i/o completion):
非同步過程呼叫
(apcs)問題:
只有發完成埠
(i/o completion)的優點:
不會限制
handle個數,可處理成千上萬個連線。i/o completion port允許乙個執行緒將乙個請求暫時儲存下來,由另乙個執行緒為它做實際服務。
併發模型與執行緒池:
在典型的併發模型中,伺服器為每乙個客戶端建立乙個執行緒,如果很多客戶同時請求,則這些執行緒都是執行的,那麼
理解與使用:
第一步:
在我們使用完成埠之前,要呼叫
createiocompletionport函式先建立完成埠物件。
定義如下:
handle createiocompletionport(
handle filehandle,
handle existingcompletionport,
dword completionkey,
dword numberofconcurrentthreads);
filehandle:
檔案或裝置的
handle, 如果值為invalid_handle_value則產生乙個沒有和任何檔案handle有關係的port.( 可以用來和完成埠聯絡的各種控制代碼,檔案,套接字
)existingcompletionport:
null時生成乙個新
port, 否則handle會加到此port上。
completionkey:
使用者自定義數值,被交給服務的執行緒。getqueuedcompletionstatus函式時我們可以完全得到我們在此聯絡函式中的完成鍵(申請的記憶體塊)。在
getqueuedcompletionstatus
中可以完封不動的得到這個記憶體塊,並且使用它。
numberofconcurrentthreads:
引數 numberofconcurrentthreads用來指定在乙個完成埠上可以併發的執行緒數量。理想的情況是,乙個處理器上只執行乙個執行緒,這樣可以避免執行緒上下文切換的開銷。如果這個引數的值為0,那就是告訴系統執行緒數與處理器數相同。我們可以用下面的**來建立i/o完成埠。
隱藏在之建立完成埠的秘密:
1. 建立乙個完成埠
createiocompletionport(invalid_handle_value, 0, 0, dwnumberofconcurrentthreads);
2. 裝置列表,完成埠把它同乙個或多個裝置相關聯。
createiocompletionport(hdevice, hcompport, dwcompkey, 0) ;
第二步:
根據處理器個數,建立
cpu*2個工作執行緒:
createthread(null, 0, serverworkerthread, completionport,0, &threadid))
與此同時,伺服器呼叫
wsasocket,bind, listen, wsaaccept,之後,呼叫
createiocompletionport((handle) accept, completionport... )把乙個套接字控制代碼和乙個完成埠繫結到一起。完成埠又同乙個或多個裝置相關聯著,所以以套接字為基礎,投遞傳送和請求,對
i/o處理。接著,可以依賴完成埠,接收有關i/o操作完成情況的通知。再看程式裡:
wsarecv(accept, &(periodata->databuf), 1, &recvbytes, &flags,
i/o,所以wsasend和wsarecv的呼叫會立即返回。
系統處理:
當乙個裝置的非同步
i/o請求完成之後,系統會檢查該裝置是否關聯了乙個完成埠,如果是,系統就向該完成埠的i/o完成佇列中加入完成的i/o請求列。
然後我們需要從這個完成佇列中,取出呼叫後的結果
工作執行緒與完成埠:
和非同步過程呼叫不同
(在乙個
getqueuedcompletionstatus
在工作執行緒內呼叫
getqueuedcompletionstatus函式。
getqueuedcompletionstatus(
handle completionport,
lpdword lpnumberofbytestransferred,
lpdword lpcompletionkey,
dword dwmilliseconds
);completionport:指出了執行緒要監視哪乙個完成埠。很多服務應用程式只是使用乙個
i/o完成埠,所有的i/o請求完成以後的通知都將發給該埠。
lpnumberofbytestransferred:傳輸的資料位元組數
lpcompletionkey:
完成埠的單句柄資料指標,這個指標將可以得到我們在
createiocompletionport中申請那片記憶體。
重疊 i/o請求結構,這個結構同樣是指向我們在重疊請求時所申請的記憶體塊,同時和lpcompletionkey,一樣我們也可以利用這個記憶體塊來儲存我們要儲存的任意資料。
dwmilliseconds:
等待的最長時間
getqueuedcompletionstatus功能及隱藏的秘密:
getqueuedcompletionstatus使呼叫執行緒掛起,直到指定的埠的
i/o完成佇列中出現了一項或直到超時。(i/0完成佇列中出現了記錄)呼叫
getqueuedcompletionstatus時,呼叫執行緒的id(cpu*2個執行緒,每個serverworkerthread的執行緒id)就被放入該等待執行緒佇列中。
等待執行緒佇列很簡單,只是儲存了這些執行緒的id。完成埠會按照後進先出的原則將乙個執行緒佇列的id放入到釋放執行緒列表中。
這樣, i/o完成埠核心物件就知道哪些執行緒正在等待處理完成的i/o請求。當埠的i/o完成佇列出現一項時,完成埠就喚醒(睡眠狀態中變為可排程狀態)等待執行緒佇列中的乙個執行緒。執行緒將得到完成
getqueuedcompletionstatus返回可能有多種原因,如果傳遞無效完成埠控制代碼,函式返回
false,getlasterror返回乙個錯誤(error_invalid_handle),如果超時,返回false, getlasterror返回wait_timeout, i/o完成佇列刪除一項,該表項是乙個成功完成的i/o請求,則返回true。
呼叫 getqueuedcompletionstatus的執行緒是後進先出的方式喚醒的,比如有4個執行緒等待,如果有乙個i/o,最後乙個呼叫getqueuedcompletionstatus的執行緒被喚醒來處理。處理完之後,再呼叫getqueuedcompletionstatus進入等待執行緒佇列中。
深入分析完成埠執行緒池排程原理:
假設我們執行在
2cpu的機器上。建立完成埠時指定2個併發,建立了4個工作執行緒加入執行緒池中等待完成i/o請求,且完成埠佇列(先入先出)中有3個完成i/o的請求的情況:
工作執行緒執行
, 建立了4個工作執行緒,呼叫getqueuedcompletionstatus時,該呼叫執行緒就進入了睡眠狀態,假設這個時候,i/o完成佇列出現了三項,呼叫執行緒的id就被放入該等待執行緒佇列中。
i/o完成埠核心物件(第
3個引數等級執行緒佇列),因此知道哪些執行緒正在等待處理完成的i/o請求。當埠的i/o完成佇列出現一項時,完成埠就喚醒(睡眠狀態中變為可排程狀態)等待執行緒佇列中的乙個執行緒
在前面我們指定了併發執行緒的數目是
2,所以i/o完成埠喚醒2個執行緒,執行緒d和執行緒c,另兩個繼續休眠(執行緒b,執行緒a),直到執行緒d處理完了,發現表項裡還有要處理的,就喚醒同一執行緒繼續處理。
執行緒併發量:
併發量限制了與該完成埠相關聯的可執行執行緒的數目
, 它類似閥門的作用。 當與該完成埠相關聯的可執行執行緒的總數目達到了該併發量,系統就會阻塞任何與該完成埠相關聯的後續執行緒的執行, 直到與該完成埠相關聯的可執行執行緒數目下降到小於該併發量為止。所以解釋了執行緒池中的執行執行緒可能會比設定的併發執行緒多的原因。
它的作用
: 最有效的假想是發生在有完成包在佇列中等待,而沒有等待被滿足,因為此時完成埠達到了其併發量的極限。此時,乙個正在執行中的執行緒呼叫
getqueuedcompletionstatus時,它就會立刻從佇列中取走該完成包。這樣就不存在著環境的切換,因為該處於執行中的執行緒就會連續不斷地從佇列中取走完成包,而其他的執行緒就不能執行了 。
注意:如果池中的所有執行緒都在忙,客戶請求就可能拒絕,所以要適當調整這個引數,獲得最佳效能。
執行緒併發:d執行緒掛起,加入暫停執行緒,醒來後又加入釋放執行緒佇列。
執行緒的安全退出:
完成埠使用需要注意的地方:
1.
完成埠模型
最近要做乙個網路方面的小東東,基於c s模式的。都說iocp可以使系統達到最佳的效能,因此我就比劃了兩下,獻醜了。抄書開始。從本質上說,完成埠模型要求建立乙個windows完成埠物件,該物件通過指定數量的執行緒,對重疊i o請求進行管理,以便為已經完成的重疊i o請求提供服務。首先要建立乙個i o完...
C I O完成埠的實現
在 vc 中我幾乎每乙個 windows service 都是採用 i o完成埠。至於在 c 中如何使用 i o完成埠,一直很少見人提及。william kennedy 的三篇文章 iocp thread pooling in c 對實現這種機制很有幫助,唯一美中不足的是,它只能把 int數值壓入完...
完成埠iocp和重疊埠
windows下的iocp和 linux下的epoll epoll我還沒有研究過 都是屬於socket 網路程式設計的範疇.不過其特色是 用固定很少的執行緒去管理成千上萬個socket連線.其相當於 非同步通訊 普通socket是同步,同步 可能要乙個執行緒乙個socket鏈結 去做,由於系統可以執...