實現(xiàn)一個高性能的服務(wù)應(yīng)用依賴于一個高性能的線程模型寇仓。線程太多或太少都會引起性能問題惊橱。舉一個極端的例子局荚,如果一個服務(wù)只用一個線程處理所有的用戶請求,性能會很糟糕钞澳,因為受限于一個線程同一時間只能處理一個請求怠惶。當(dāng)然,一個線程是可以同時處理多個請求的轧粟,需要I/O的時候就切換策治,但是這樣會引入具大的復(fù)雜性并且也不能利用好電腦的多個cpu脓魏。另一個極端是,服務(wù)創(chuàng)建一個大的線程池通惫,可以讓每個請求對應(yīng)一個單獨的線程茂翔。這會導(dǎo)致線程抖動問題。大量線程被喚醒履腋,做一些cpu操作珊燎,然后阻塞在I/O上,處理完成后府树,再阻塞以等待下次請求。如無意外料按,調(diào)度器會分隔cpu 時間奄侠,并引起上下文切換。
我們的目標(biāo)是载矿,盡量避免線程阻塞垄潮,盡可能少的引起上下文切換,同時最大化利用多個線程的并發(fā)機(jī)制闷盔。理想情況是弯洗,每一個cpu都有一個線程處理用戶請求,當(dāng)這些線程處理完請求后不阻塞因為剛好有等待的請求需要處理逢勾。如果想讓電腦按我們設(shè)想的工作牡整,必須有一套機(jī)制,當(dāng)一個線程因為一個用戶請求被阻塞在I/O上的時候溺拱,激活另一個線程逃贝。
Windows NT 3.5 介紹了一些api可以使我們相對簡單的完成這個任務(wù)。這些API圍繞著一個對象叫做completion port 迫摔。在這篇文章中沐扳,我會概述completion port如何使用,以及Windows實現(xiàn)他們的底層原理句占。
應(yīng)用利用completion port 作為多個文件操作符I/O完成時的焦點沪摄。當(dāng)一個文件和一個completion port對應(yīng)起來的時候,任何異步I/O操作完成纱烘,會以隊列的方式發(fā)一個completion包給這個port杨拐。所以一個線程可以簡單的等待多個文件操作完成。只要適當(dāng)?shù)目刂凭€程的數(shù)量擂啥,就可以發(fā)揮系統(tǒng)最大的性能戏阅。
當(dāng)應(yīng)用創(chuàng)建一個completion port的時候,會指定一個并發(fā)值啤它。這個值是completion port對應(yīng)的最大的可以及時的運行的線程數(shù)奕筐。像我之前描述的那樣舱痘,最理想的是同一時間一個線程對應(yīng)一個cpu。這個并發(fā)值被windows用來控制多少個線程可以被激活离赫,如果活躍的線程已經(jīng)等于這個并發(fā)值了芭逝,那么windows將不會再允許completion port再多個線程運行。一個線程處理處理完成一個請求渊胸,然后檢查一下隊列里有沒有completion包旬盯,如果有就處理,這個過程中翎猛,沒有上下文切換胖翰。
completion port的工作流見下圖
原文見
http://sysinternals.d4rk4.ru/Information/IoCompletionPorts.html