• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 58,  comments - 75,  trackbacks - 0
            8 前攝器( Proactor ):用于為異步事件多路分離和分派處理器的對(duì)象行為模式
             
            Irfan Pyarali ? Tim Harrison?? Douglas C. Schmidt?? Thomas D. Jordan
             
             
            現(xiàn)代操作系統(tǒng)為開發(fā)并發(fā)應(yīng)用提供了多種機(jī)制。同步多線程是一種流行的機(jī)制,用于開發(fā)同時(shí)執(zhí)行多個(gè)操作的應(yīng)用。但是,線程常常有很高的性能開銷,并且需要對(duì)同步模式和原理有深入的了解。因此,有越來越多的操作系統(tǒng)支持異步機(jī)制,在減少多線程的大量開銷和復(fù)雜性的同時(shí),提供了并發(fā)的好處。
            本論文中介紹的前攝器(Proactor )模式描述怎樣構(gòu)造應(yīng)用和系統(tǒng),以有效地利用操作系統(tǒng)支持的異步機(jī)制。當(dāng)應(yīng)用調(diào)用異步操作時(shí),OS 代表應(yīng)用執(zhí)行此操作。這使得應(yīng)用可以讓多個(gè)操作同時(shí)運(yùn)行,而又不需要應(yīng)用擁有相應(yīng)數(shù)目的線程。因此,通過使用更少的線程和有效利用OS 對(duì)異步操作的支持,前攝器模式簡(jiǎn)化了并發(fā)編程,并改善了性能。
             
            8.1 意圖
             
            前攝器模式支持多個(gè)事件處理器的多路分離和分派,這些處理器由異步事件的完成來觸發(fā)。通過集成完成事件(completion event)的多路分離和相應(yīng)的事件處理器的分派,該模式簡(jiǎn)化了異步應(yīng)用的開發(fā)。
             
            8.2 動(dòng)機(jī)
             
            這一部分提供使用前攝器模式的上下文和動(dòng)機(jī)。
             
            8.2.1 上下文和壓力
             
            前攝器模式應(yīng)該被用于應(yīng)用需要并發(fā)執(zhí)行操作的性能好處、又不想受到同步多線程或反應(yīng)式編程的約束時(shí)。為說明這些好處,設(shè)想一個(gè)需要并發(fā)執(zhí)行多個(gè)操作的網(wǎng)絡(luò)應(yīng)用。例如,一個(gè)高性能Web服務(wù)器必須并發(fā)處理發(fā)送自多個(gè)客戶的HTTP請(qǐng)求[1, 2]。圖8-1 顯示了Web瀏覽器和Web服務(wù)器之間的典型交互。當(dāng)用戶指示瀏覽器打開一個(gè)URL時(shí),瀏覽器發(fā)送一個(gè)HTTP GET請(qǐng)求給Web服務(wù)器。收到請(qǐng)求,服務(wù)器就解析并校驗(yàn)請(qǐng)求,并將指定的文件發(fā)回給瀏覽器。
             
             
            圖8-1 典型的Web服務(wù)器通信軟件體系結(jié)構(gòu)
             
            開發(fā)高性能Web服務(wù)器要求消除以下壓力:
             
            • 并發(fā)性:服務(wù)器必須同時(shí)執(zhí)行多個(gè)客戶請(qǐng)求;
            • 效率:服務(wù)器必須最小化響應(yīng)延遲、最大化吞吐量,并避免不必要地使用CPU;
            • 編程簡(jiǎn)單性:服務(wù)器的設(shè)計(jì)應(yīng)該簡(jiǎn)化高效的并發(fā)策略的使用;
            • 可適配性:應(yīng)該使繼承新的或改進(jìn)的傳輸協(xié)議(比如HTTP 1.1[3])所帶來的維護(hù)代價(jià)最小化。
             
            Web服務(wù)器可以使用若干并發(fā)策略來實(shí)現(xiàn),包括多個(gè)同步線程、反應(yīng)式同步事件分派和前攝式異步事件分派。下面,我們檢查傳統(tǒng)方法的缺點(diǎn),并解釋前攝器模式是怎樣提供一種強(qiáng)大的技術(shù),為高性能并發(fā)應(yīng)用而支持高效、靈活的異步事件分派策略的。
             
            8.2.2 傳統(tǒng)并發(fā)模型的常見陷阱和缺陷
             
            同步的多線程和反應(yīng)式編程是實(shí)現(xiàn)并發(fā)的常用方法。這一部分描述這些編程模型的缺點(diǎn)。
             
            8.2.2.1 通過多個(gè)同步線程實(shí)現(xiàn)的并發(fā)
             
            或許最為直觀的實(shí)現(xiàn)并發(fā)Web服務(wù)器的途徑是使用同步的多線程。在此模型中,多個(gè)服務(wù)器線程同時(shí)處理來自多個(gè)客戶的HTTP GET請(qǐng)求。每個(gè)線程同步地執(zhí)行連接建立、HTTP請(qǐng)求讀取、請(qǐng)求解析和文件傳輸操作。作為結(jié)果,每個(gè)操作都阻塞直到完成。
            同步線程的主要優(yōu)點(diǎn)是應(yīng)用代碼的簡(jiǎn)化。特別是,Web服務(wù)器為服務(wù)客戶A的請(qǐng)求所執(zhí)行的操作在很大程度上獨(dú)立于為服務(wù)客戶B的請(qǐng)求所需的操作。因而,很容易在分離的線程中對(duì)不同的請(qǐng)求進(jìn)行服務(wù),因?yàn)樵诰€程之間共享的狀態(tài)數(shù)量很少;這也最小化了對(duì)同步的需要。而且,在分離的線程中執(zhí)行應(yīng)用邏輯也使得開發(fā)者可以使用直觀的順序命令和阻塞操作。
             
             
            圖8-2 多線程Web服務(wù)器體系結(jié)構(gòu)
             
            圖8-2顯示使用同步線程來設(shè)計(jì)的Web服務(wù)器怎樣并發(fā)地處理多個(gè)客戶請(qǐng)求。該圖顯示的Sync Acceptor對(duì)象封裝服務(wù)器端用于同步接受網(wǎng)絡(luò)連接的機(jī)制。使用“Thread Per Connection”并發(fā)模型,各個(gè)線程為服務(wù)HTTP GET請(qǐng)求所執(zhí)行的一系列步驟可被總結(jié)如下:
             
            1. 每個(gè)線程同步地阻塞在accept socket調(diào)用中,等待客戶連接請(qǐng)求;
            2. 客戶連接到服務(wù)器,連接被接受;
            3. 新客戶的HTTP請(qǐng)求被同步地從網(wǎng)絡(luò)連接中讀取;
            4. 請(qǐng)求被解析;
            5. 所請(qǐng)求的文件被同步地讀取;
            6. 文件被同步地發(fā)送給客戶。
             
            附錄A.1中有一個(gè)將同步線程模型應(yīng)用于Web服務(wù)器的C++代碼例子。
            如上所述,每個(gè)并發(fā)地連接的客戶由一個(gè)專用的服務(wù)器線程服務(wù)。在繼續(xù)為其他HTTP請(qǐng)求服務(wù)之前,該線程同步地完成一個(gè)被請(qǐng)求的操作。因此,要在服務(wù)多個(gè)客戶時(shí)執(zhí)行同步I/O,Web服務(wù)器必須派生多個(gè)線程。盡管這種同步線程模式是直觀的,且能夠相對(duì)高效地映射到多CPU平臺(tái)上,它還是有以下缺點(diǎn):
             
            線程策略與并發(fā)策略被緊耦合:這種體系結(jié)構(gòu)要求每個(gè)相連客戶都有一個(gè)專用的線程。通過針對(duì)可用資源(比如使用線程池來對(duì)應(yīng)CPU的數(shù)目)、而不是正被并發(fā)服務(wù)的客戶的數(shù)目來調(diào)整其線程策略,可能會(huì)更好地優(yōu)化一個(gè)并發(fā)應(yīng)用;
             
            更大的同步復(fù)雜性:線程可能會(huì)增加序列化對(duì)服務(wù)器的共享資源(比如緩存文件和Web頁面點(diǎn)擊日志)的訪問所必需的同步機(jī)制的復(fù)雜性;
             
            更多的性能開銷:由于上下文切換、同步和CPU間的數(shù)據(jù)移動(dòng)[4],線程的執(zhí)行可能很低效;
             
            不可移植性:線程有可能在有些平臺(tái)上不可用。而且,根據(jù)對(duì)占先式和非占先式線程的支持,OS平臺(tái)之間的差異非常大。因而,很難構(gòu)建能夠跨平臺(tái)統(tǒng)一運(yùn)作的多線程服務(wù)器。
             
            作為這些缺點(diǎn)的結(jié)果,多線程常常不是開發(fā)并發(fā)Web服務(wù)器的最為高效的、也不是最不復(fù)雜的解決方案。
             
            8.2.2.2 通過反應(yīng)式同步事件分派實(shí)現(xiàn)的并發(fā)
             
            另一種實(shí)現(xiàn)同步Web服務(wù)器的常用方法是使用反應(yīng)式事件分派模型。反應(yīng)堆(Reactor)模式描述應(yīng)用怎樣將Event Handler登記到Initiation Dispatcher。Initiation Dispatcher通知Event Handler何時(shí)能發(fā)起一項(xiàng)操作而不阻塞。
            單線程并發(fā)Web服務(wù)器可以使用反應(yīng)式事件分派模型,它在一個(gè)事件循環(huán)中等待Reactor通知它發(fā)起適當(dāng)?shù)牟僮鳌eb服務(wù)器中反應(yīng)式操作的一個(gè)例子是Acceptor(接受器)[6]到Initiation Dispatcher的登記。當(dāng)數(shù)據(jù)在網(wǎng)絡(luò)連接上到達(dá)時(shí),分派器回調(diào)Acceptor,后者接受網(wǎng)絡(luò)連接,并創(chuàng)建HTTP Handler。于是這個(gè)HTTP Handler就登記到Reactor,以在Web服務(wù)器的單線程控制中處理在那個(gè)連接上到來的URL請(qǐng)求。
            圖8-3和圖8-4顯示使用反應(yīng)式事件分派設(shè)計(jì)的Web服務(wù)器怎樣處理多個(gè)客戶。圖8-3顯示當(dāng)客戶連接到Web服務(wù)器時(shí)所采取的步驟。圖8-4顯示W(wǎng)eb服務(wù)器怎樣處理客戶請(qǐng)求。圖8-3的一系列步驟可被總結(jié)如下:
             
             
            圖8-3 客戶連接到反應(yīng)式Web服務(wù)器
             
             
            圖8-4 客戶發(fā)送HTTP請(qǐng)求到反應(yīng)式Web服務(wù)器
             
            1. Web服務(wù)器將Acceptor登記到Initiation Dispatcher,以接受新連接;
            2. Web服務(wù)器調(diào)用Initiation Dispatcher的事件循環(huán);
            3. 客戶連接到Web服務(wù)器;
            4. Initiation Dispatcher將新連接請(qǐng)求通知Acceptor,后者接受新連接;
            5. Acceptor創(chuàng)建HTTP Handler,以服務(wù)新客戶;
            6. HTTP Handler將連接登記到Initiation Dispatcher,以讀取客戶請(qǐng)求數(shù)據(jù)(就是說,在連接變得“讀就緒”時(shí));
            7. HTTP Handler服務(wù)來自新客戶的請(qǐng)求。
             
            圖8-4顯示反應(yīng)式Web服務(wù)器為服務(wù)HTTP GET請(qǐng)求所采取的一系列步驟。該過程描述如下:
             
            1. 客戶發(fā)送HTTP GET請(qǐng)求;
            2. 當(dāng)客戶請(qǐng)求數(shù)據(jù)到達(dá)服務(wù)器時(shí),Initiation Dispatcher通知HTTP Handler;
            3. 請(qǐng)求以非阻塞方式被讀取,于是如果操作會(huì)導(dǎo)致調(diào)用線程阻塞,讀操作就返回EWOULDBLOCK(步驟2和3將重復(fù)直到請(qǐng)求被完全讀取);
            4. HTTP Handler解析HTTP請(qǐng)求;
            5. 所請(qǐng)求的文件從文件系統(tǒng)中被同步讀取;
            6. 為發(fā)送文件數(shù)據(jù)(就是說,當(dāng)連接變得“寫就緒”時(shí)),HTTP Handler將連接登記到Initiation Dispatcher;
            7. 當(dāng)TCP連接變得寫就緒時(shí),Initiation Dispatcher通知HTTP Handler;
            8. HTTP Handler以非阻塞方式將所請(qǐng)求文件發(fā)送給客戶,于是如果操作會(huì)導(dǎo)致調(diào)用線程阻塞,寫操作就返回EWOULDBLOCK(步驟7和8將重復(fù)直到數(shù)據(jù)被完全遞送)。
             
            附錄A.2中有一個(gè)將反應(yīng)式事件分派模型應(yīng)用于Web服務(wù)器的C++代碼例子。
            因?yàn)镮nitiation Dispatcher運(yùn)行在單線程中,網(wǎng)絡(luò)I/O操作以非阻塞方式運(yùn)行在Reactor的控制之下。如果當(dāng)前操作的進(jìn)度停止了,操作就被轉(zhuǎn)手給Initiation Dispatcher,由它監(jiān)控系統(tǒng)操作的狀態(tài)。當(dāng)操作可以再度前進(jìn)時(shí),適當(dāng)?shù)腅vent Handler會(huì)被通知。
            反應(yīng)式模式的主要優(yōu)點(diǎn)是可移植性,粗粒度并發(fā)控制帶來的低開銷(就是說,單線程不需要同步或上下文切換),以及通過使應(yīng)用邏輯與分派機(jī)制去耦合所獲得的模塊性。但是,該方法有以下缺點(diǎn):
             
            復(fù)雜的編程:如從前面的列表所看到的,程序員必須編寫復(fù)雜的邏輯,以保證服務(wù)器不會(huì)在服務(wù)一個(gè)特定客戶時(shí)阻塞。
             
            缺乏多線程的OS 支持:大多數(shù)操作系統(tǒng)通過select系統(tǒng)調(diào)用[7]來實(shí)現(xiàn)反應(yīng)式分派模型。但是,select不允許多于一個(gè)的線程在同一個(gè)描述符集上等待。這使得反應(yīng)式模型不適用于高性能應(yīng)用,因?yàn)樗鼪]有有效地利用硬件的并行性。
             
            可運(yùn)行任務(wù)的調(diào)度:在支持占先式線程的同步多線程體系結(jié)構(gòu)中,將可運(yùn)行線程調(diào)度并時(shí)分(time-slice)到可用CPU上是操作系統(tǒng)的責(zé)任。這樣的調(diào)度支持在反應(yīng)式體系結(jié)構(gòu)中不可用,因?yàn)樵趹?yīng)用中只有一個(gè)線程。因此,系統(tǒng)的開發(fā)者必須小心地在所有連接到Web服務(wù)器的客戶之間將線程分時(shí)。這只能通過執(zhí)行短持續(xù)時(shí)間、非阻塞的操作來完成。
             
            作為這些缺點(diǎn)的結(jié)果,當(dāng)硬件并行可用時(shí),反應(yīng)式事件分派不是最為高效的模型。由于需要避免使用阻塞I/O,該模式還有著相對(duì)較高的編程復(fù)雜度。
             
            8.2.3 解決方案:通過前攝式操作實(shí)現(xiàn)的并發(fā)
             
            當(dāng)OS平臺(tái)支持異步操作時(shí),一種高效而方便的實(shí)現(xiàn)高性能Web服務(wù)器的方法是使用前攝式事件分派。使用前攝式事件分派模型設(shè)計(jì)的Web服務(wù)器通過一或多個(gè)線程控制來處理異步操作的完成。這樣,通過集成完成事件多路分離(completion event demultiplexing)和事件處理器分派,前攝器模式簡(jiǎn)化了異步的Web服務(wù)器
            異步的Web服務(wù)器將這樣來利用前攝器模式:首先讓W(xué)eb服務(wù)器向OS發(fā)出異步操作,并將回調(diào)方法登記到Completion Dispatcher(完成分派器),后者將在操作完成時(shí)通知Web服務(wù)器。于是OS代表Web服務(wù)器執(zhí)行操作,并隨即在一個(gè)周知的地方將結(jié)果排隊(duì)。Completion Dispatcher負(fù)責(zé)使完成通知出隊(duì),并執(zhí)行適當(dāng)?shù)摹⒑袘?yīng)用特有的Web服務(wù)器代碼的回調(diào)。
             
             
            圖8-5 客戶連接到基于前攝器的Web服務(wù)器
             
             
            圖8-6 客戶發(fā)送請(qǐng)求給基于前攝器的Web服務(wù)器
             
            圖8-5和圖8-6顯示使用前攝式事件分派設(shè)計(jì)的Web服務(wù)器怎樣在一或多個(gè)線程中并發(fā)地處理多個(gè)客戶。圖8-5顯示當(dāng)客戶連接到Web服務(wù)器時(shí)所采取的一系列步驟。
             
            1. Web服務(wù)器指示Acceptor發(fā)起異步接受;
            2. 接受器通過OS發(fā)起異步接受,將其自身作為Completion Handler和Completion Dispatcher的引用傳遞;并將用于在異步接受完成時(shí)通知Acceptor;
            3. Web服務(wù)器調(diào)用Completion Dispatcher的事件循環(huán);
            4. 客戶連接到Web服務(wù)器;
            5. 當(dāng)異步接受操作完成時(shí),操作系統(tǒng)通知Completion Dispatcher;
            6. Completion Dispatcher通知接受器;
            7. Acceptor創(chuàng)建HTTP Handler;
            8. HTTP Handler發(fā)起異步操作,以讀取來自客戶的請(qǐng)求數(shù)據(jù),并將其自身作為Completion Handler和Completion Dispatcher的引用傳遞;并將用于在異步讀取完成時(shí)通知HTTP Handler。
             
            圖8-6 顯示前攝式Web服務(wù)器為服務(wù)HTTP GET請(qǐng)求所采取的步驟。這些步驟解釋如下:
             
            1. 客戶發(fā)送HTTP GET請(qǐng)求;
            2. 讀取操作完成,操作系統(tǒng)通知Completion Dispatcher;
            3. Completion Dispatcher通知HTTP Handler(步驟2和3將重復(fù)直到整個(gè)請(qǐng)求被接收);
            4. HTTP Handler解析請(qǐng)求;
            5. HTTP Handler同步地讀取所請(qǐng)求的文件;
            6. HTTP Handler發(fā)起異步操作,以把文件數(shù)據(jù)寫到客戶連接,并將其自身作為Completion Handler和Completion Dispatcher的引用傳遞;并將用于在異步寫入完成時(shí)通知HTTP Handler。
            7. 當(dāng)寫操作完成時(shí),操作系統(tǒng)通知Completion Dispatcher;
            8. 隨后Completion Dispatcher通知Completion Handler(步驟6-8將重復(fù)直到文件被完全遞送)。
             
            8.8中有一個(gè)將前攝式事件分派模型應(yīng)用于Web服務(wù)器的C++代碼例子。
            使用前攝器模式的主要優(yōu)點(diǎn)是可以啟動(dòng)多個(gè)并發(fā)操作,并可并行運(yùn)行,而不要求應(yīng)用必須擁有多個(gè)線程。操作被應(yīng)用異步地啟動(dòng),它們?cè)贠S的I/O子系統(tǒng)中運(yùn)行直到完成。發(fā)起操作的線程現(xiàn)在可以服務(wù)另外的請(qǐng)求了。
            例如,在上面的例子中,Completion Dispatcher可以是單線程的。當(dāng)HTTP請(qǐng)求到達(dá)時(shí),單個(gè)Completion Dispatcher線程解析請(qǐng)求,讀取文件,并發(fā)送響應(yīng)給客戶。因?yàn)轫憫?yīng)是被異步發(fā)送的,多個(gè)響應(yīng)就有可能同時(shí)被發(fā)送。而且,同步的文件讀取可以被異步的文件讀取取代,以進(jìn)一步增加并發(fā)的潛力。如果文件讀取是被異步完成的,HTTP Handler所執(zhí)行的唯一的同步操作就只剩下了HTTP協(xié)議請(qǐng)求解析。
            前攝式模型的主要缺點(diǎn)是編程邏輯至少和反應(yīng)式模型一樣復(fù)雜。而且,前攝器模式可能會(huì)難以調(diào)試,因?yàn)楫惒讲僮鞒3S兄豢深A(yù)測(cè)和不可重復(fù)的執(zhí)行序列,這就使分析和調(diào)試復(fù)雜化了。8.7描述怎樣應(yīng)用其他模式(比如異步完成令牌[8])來簡(jiǎn)化異步應(yīng)用編程模型。
             
            8.3 適用性
             
            當(dāng)具有以下一項(xiàng)或多項(xiàng)條件時(shí)使用前攝器模式:
             
            • 應(yīng)用需要執(zhí)行一個(gè)或多個(gè)不阻塞調(diào)用線程的異步操作;
            • 當(dāng)異步操作完成時(shí)應(yīng)用必須被通知;
            • 應(yīng)用需要獨(dú)立于它的I/O模型改變它的并發(fā)策略;
            • 通過使依賴于應(yīng)用的邏輯與應(yīng)用無關(guān)的底層構(gòu)造去耦合,應(yīng)用將從中獲益;
            • 當(dāng)使用多線程方法或反應(yīng)式分派方法時(shí),應(yīng)用的執(zhí)行將很低效,或是不能滿足性能需求。
             
            8.4 結(jié)構(gòu)和參與者
             
            在圖8-7中使用OMT表示法演示了前攝器模式的結(jié)構(gòu)。
            前攝器模式中的關(guān)鍵參與者包括:
             
            前攝發(fā)起器(Proactive Initiator。Web服務(wù)器應(yīng)用的主線程):
             
            • Proactive Initiator是應(yīng)用中任何發(fā)起Asynchronous Operation(異步操作)的實(shí)體。它將Completion Handler和Completion Dispatcher登記到Asynchronous Operation Processor(異步操作處理器),此處理器在操作完成時(shí)通知前攝發(fā)起器。
             
            完成處理器(Completion Handler。Acceptor和HTTP Handler):
             
            • 前攝器模式將應(yīng)用所實(shí)現(xiàn)的Completion Handler接口用于Asynchronous Operation完成通知。
             
            異步操作(Asynchronous Operation。Async_Read、Async_Write和Async_Accept方法):
             
            • Asynchronous Operation被用于代表應(yīng)用執(zhí)行請(qǐng)求(比如I/O和定時(shí)器操作)。當(dāng)應(yīng)用調(diào)用Asynchronous Operation時(shí),操作的執(zhí)行沒有借用應(yīng)用的線程控制。因此,從應(yīng)用的角度來看,操作是被異步地執(zhí)行的。當(dāng)Asynchronous Operation完成時(shí),Asynchronous Operation Processor將應(yīng)用通知委托給Completion Dispatcher。
             
            異步操作處理器(Asynchronous Operation Processor。操作系統(tǒng)):
             
            • Asynchronous Operation是由Asynchronous Operation Processor來運(yùn)行直至完成的。該組件通常由OS實(shí)現(xiàn)。
             
            完成分派器(Completion Dispatcher。Notification Queue):
             
            • Completion Dispatcher負(fù)責(zé)在Asynchronous Operation完成時(shí)回調(diào)應(yīng)用的Completion Handler。當(dāng)Asynchronous Operation Processor完成異步發(fā)起的操作時(shí),Completion Dispatcher代表應(yīng)用執(zhí)行應(yīng)用回調(diào)。
             
             
            圖8-7 前攝器模式中的參與者
             
            8.5 協(xié)作
             
            有若干良好定義的步驟被用于所有Asynchronous Operation。在高水平的抽象上,應(yīng)用異步地發(fā)起操作,并在操作完成時(shí)被通知。圖8-8顯示在模式參與者之間必定發(fā)生的下列交互:
             
            1. 前攝發(fā)起器發(fā)起操作:為執(zhí)行異步操作,應(yīng)用在Asynchronous Operation Processor上發(fā)起操作。例如,Web服務(wù)器可能要求OS在網(wǎng)絡(luò)上使用特定的socket連接傳輸文件。要請(qǐng)求這樣的操作,Web服務(wù)器必須指定要使用哪一個(gè)文件和網(wǎng)絡(luò)連接。而且,Web服務(wù)器必須指定(1)當(dāng)操作完成時(shí)通知哪一個(gè)Completion Handler,以及(2)一旦文件被傳輸,哪一個(gè)Completion Dispatcher應(yīng)該執(zhí)行回調(diào)。
            2. 異步操作處理器執(zhí)行操作:當(dāng)應(yīng)用在Asynchronous Operation Processor上調(diào)用操作時(shí),它相對(duì)于其他應(yīng)用操作異步地運(yùn)行這些操作。現(xiàn)代操作系統(tǒng)(比如Solaris和Windows NT)在內(nèi)核中提供異步的I/O子系統(tǒng)。
            3. 異步操作處理器通知完成分派器:當(dāng)操作完成時(shí),Asynchronous Operation Processor取得在操作被發(fā)起時(shí)指定的Completion Handler和Completion Dispatcher。隨后Asynchronous Operation Processor將Asynchronous Operation的結(jié)果和Completion Handler傳遞給Completion Dispatcher,以用于回調(diào)。例如,如果文件已被異步傳輸,Asynchronous Operation Processor可以報(bào)告完成狀態(tài)(比如成功或失敗),以及寫入網(wǎng)絡(luò)連接的字節(jié)數(shù)。
            4. 完成分派器通知應(yīng)用:Completion Dispatcher在Completion Handler上調(diào)用完成掛鉤,將由應(yīng)用指定的任何完成數(shù)據(jù)傳遞給它。例如,如果異步讀取完成,通常一個(gè)指向新到達(dá)數(shù)據(jù)的指針將會(huì)被傳遞給Completion Handler。
             
             
            圖8-8 前攝器模式的交互圖
             
            8.6 效果
             
            這一部分詳述使用前攝器模式的效果。
             
            8.6.1 好處
             
            前攝器模式提供以下好處:
             
            增強(qiáng)事務(wù)分離:前攝器模式使應(yīng)用無關(guān)的異步機(jī)制與應(yīng)用特有的功能去耦合。應(yīng)用無關(guān)的機(jī)制成為可復(fù)用組件,知道怎樣多路分離與Asynchronous Operation相關(guān)聯(lián)的完成事件,并分派適當(dāng)?shù)挠蒀ompletion Handler定義的回調(diào)方法。同樣地,應(yīng)用特有的功能知道怎樣執(zhí)行特定類型的服務(wù)(比如HTTP處理)。
             
            改善應(yīng)用邏輯可移植性:通過允許接口獨(dú)立于執(zhí)行事件多路分離的底層OS調(diào)用而復(fù)用,它改善了應(yīng)用的可移植性。這些系統(tǒng)調(diào)用檢測(cè)并報(bào)告可能同時(shí)發(fā)生在多個(gè)事件源之上的事件。事件源可以是I/O端口、定時(shí)器、同步對(duì)象、信號(hào),等等。在實(shí)時(shí)POSIX平臺(tái)上,異步I/O函數(shù)由aio API族[9]提供。在Windows NT中,I/O完成端口和重疊式(overlapped)I/O被用于實(shí)現(xiàn)異步I/O[10]。
             
            完成分派器封裝了并發(fā)機(jī)制:使Completion Dispatcher與Asynchronous Operation Processor去耦合的一個(gè)好處是應(yīng)用可以通過多種并發(fā)策略來配置Completion Dispatcher,而不會(huì)影響其他參與者。如8.7所討論的,Completion Dispatcher可被配置使用包括單線程和線程池方案在內(nèi)的若干并發(fā)策略。
             
            線程策略被與并發(fā)策略去耦合:因?yàn)锳synchronous Operation Processor代表Proactive Initiator完成可能長(zhǎng)時(shí)間運(yùn)行的操作,應(yīng)用不會(huì)被迫派生線程來增加并發(fā)。這使得應(yīng)用可以獨(dú)立于它的線程策略改變它的并發(fā)策略。例如,Web服務(wù)器可能只想每個(gè)CPU有一個(gè)線程,但又想同時(shí)服務(wù)更多數(shù)目的客戶。
             
            提高性能:多線程操作系統(tǒng)執(zhí)行上下文切換,以在多個(gè)線程控制中輪換。雖然執(zhí)行一次上下文切換的時(shí)間保持相當(dāng)?shù)暮愣ǎ绻鸒S上下文要切換到空閑線程的話,在大量線程間輪換的總時(shí)間可以顯著地降低應(yīng)用性能。例如,線程可以輪詢OS以查看完成狀態(tài),而這是低效率的。通過只激活那些有事件要處理的合理的線程控制,前攝器模式能夠避免上下文切換的代價(jià)。例如,如果沒有待處理的GET請(qǐng)求,Web服務(wù)器不需要啟用HTTP Handler。
             
            應(yīng)用同步的簡(jiǎn)化:只要Completion Handler不派生另外的線程控制,可以不考慮、或只考慮少許同步問題而編寫應(yīng)用邏輯。Completion Handler可被編寫為就好像它們存在于一個(gè)傳統(tǒng)的單線程環(huán)境中一樣。例如,Web服務(wù)器的HTTP GET處理器可以通過Async Read操作(比如Windows NT TransmitFile函數(shù)[1])來訪問磁盤。
             
            8.6.2 缺點(diǎn)
             
            前攝器模式有以下缺點(diǎn):
             
            難以調(diào)試:以前攝器模式編寫的應(yīng)用可能難以調(diào)試,因?yàn)榉聪虻目刂屏髟跇?gòu)架基礎(chǔ)結(jié)構(gòu)和應(yīng)用特有的處理器上的回調(diào)方法之間來回振蕩。這增加了在調(diào)試器中對(duì)構(gòu)架的運(yùn)行時(shí)行為的“單步跟蹤”的困難度,因?yàn)閼?yīng)用開發(fā)者可能不了解或不能獲得構(gòu)架的代碼。這與試圖調(diào)試使用LEX和YACC編寫的編譯器的詞法分析器和解析器時(shí)所遇到的問題是類似的。在這些應(yīng)用中,當(dāng)線程控制是在用戶定義的動(dòng)作例程中時(shí),調(diào)試是相當(dāng)直接的。但是一旦線程控制返回到所生成的有限確定自動(dòng)機(jī)(Deterministic Finite Automate,DFA)骨架時(shí),就很難跟住程序邏輯了。
             
            調(diào)度和控制未完成操作:Proactive Initiator可能沒有對(duì)Asynchronous Operation的執(zhí)行順序的控制。因此,Asynchronous Operation Processor必須被小心設(shè)計(jì),以支持Asynchronous Operation的優(yōu)先級(jí)和取消處理。
             
            8.7 實(shí)現(xiàn)
             
            前攝器模式可以通過許多方式實(shí)現(xiàn)。這一部分討論實(shí)現(xiàn)前攝器模式所涉及的步驟。
             
            8.7.1 實(shí)現(xiàn)異步操作處理器
             
            實(shí)現(xiàn)前攝器模式的第一步是構(gòu)建Asynchronous Operation Processor。該組件負(fù)責(zé)代表應(yīng)用異步地執(zhí)行操作。因此,它的兩項(xiàng)主要責(zé)任是輸出Asynchronous Operation API和實(shí)現(xiàn)Asynchronous Operation Engine以完成工作。
             
            8.7.1.1 定義異步操作 API
             
            Asynchronous Operation Processor必須提供API、允許應(yīng)用請(qǐng)求Asynchronous Operation。在設(shè)計(jì)這些API時(shí)有若干壓力需要考慮:
             
            可移植性:此API不應(yīng)約束應(yīng)用或它的Proactive Initiator使用特定的平臺(tái)。
             
            靈活性:常常,異步API可以為許多類型的操作共享。例如,異步I/O操作常常被用于在多種介質(zhì)(比如網(wǎng)絡(luò)和文件)上執(zhí)行I/O。設(shè)計(jì)支持這樣的復(fù)用的API可能是有益的。
             
            回調(diào):當(dāng)操作被調(diào)用時(shí),Proactive Initiator必須登記回調(diào)。實(shí)現(xiàn)回調(diào)的一種常用方法是讓調(diào)用對(duì)象(客戶)輸出接口、讓調(diào)用者知道(服務(wù)器)。因此,Proactive Initiator必須通知Asynchronous Operation Processor,當(dāng)操作完成時(shí),哪一個(gè)Completion Handler應(yīng)被回調(diào)。
             
            完成分派器:因?yàn)閼?yīng)用可以使用多個(gè)Completion Dispatcher,Proactive Initiator還必須指示由哪一個(gè)Completion Dispatcher來執(zhí)行回調(diào)。
             
            給定所有這些問題,考慮下面的用于異步讀寫的API。Asynch_Stream類是用于發(fā)起異步讀寫的工廠。一旦構(gòu)造,可以使用此類來啟動(dòng)多個(gè)異步讀寫。當(dāng)異步讀取完成時(shí),Asynch_Stream::Read_Result將通過Completion_Handler上的handler_read回調(diào)方法被回傳給handler。類似地,當(dāng)異步寫入完成時(shí),Asynch_Stream::Write_Result將通過Completion_Handler上的handler_write回調(diào)方法被回傳給handler。
             
            class Asynch_Stream
            // = TITLE
            // A Factory for initiating reads
            // and writes asynchronously.
            {
            // Initializes the factory with information
            // which will be used with each asynchronous
            // call. <handler> is notified when the
            // operation completes. The asynchronous
            // operations are performed on the <handle>
            // and the results of the operations are
            // sent to the <Completion_Dispatcher>.
            Asynch_Stream (Completion_Handler &handler,
            HANDLE handle,
            Completion_Dispatcher *);
             
            // This starts off an asynchronous read.
            // Upto <bytes_to_read> will be read and
            // stored in the <message_block>.
            int read (Message_Block &message_block,
            u_long bytes_to_read,
            const void *act = 0);
             
            // This starts off an asynchronous write.
            // Upto <bytes_to_write> will be written
            // from the <message_block>.
            int write (Message_Block &message_block,
            u_long bytes_to_write,
            const void *act = 0);
             
            ...
            };
             
            8.7.1.2 實(shí)現(xiàn)異步操作引擎
             
            Asynchronous Operation Processor必須含有異步執(zhí)行操作的機(jī)制。換句話說,當(dāng)應(yīng)用線程調(diào)用Asynchronous Operation時(shí),必須不借用應(yīng)用的線程控制而執(zhí)行此操作。幸好,現(xiàn)代操作系統(tǒng)提供了用于Asynchronous Operation的機(jī)制(例如,POSIX 異步I/O和WinNT重疊式I/O)。在這樣的情況下,實(shí)現(xiàn)模式的這一部分只需要簡(jiǎn)單地將平臺(tái)API映射到上面描述的Asynchronous Operation API。
            如果OS平臺(tái)不提供對(duì)Asynchronous Operation的支持,有若干實(shí)現(xiàn)技術(shù)可用于構(gòu)建Asynchronous Operation Engine。或許最為直觀的解決方案是使用專用線程來為應(yīng)用執(zhí)行Asynchronous Operation。要實(shí)現(xiàn)線程化的Asynchronous Operation Engine,有三個(gè)主要步驟:
             
            1. 操作調(diào)用:因?yàn)椴僮鲗⒃谂c進(jìn)行調(diào)用的應(yīng)用線程不同的線程控制中執(zhí)行,必定會(huì)發(fā)生某種類型的線程同步。一種方法是為每個(gè)操作派生一個(gè)線程。更為常用的方法是為Asynchronous Operation Processor而管理一個(gè)專用線程池。該方法可能需要應(yīng)用線程在繼續(xù)進(jìn)行其他應(yīng)用計(jì)算之前將操作請(qǐng)求排隊(duì)。
            2. 操作執(zhí)行:既然操作將在專用線程中執(zhí)行,所以它可以執(zhí)行“阻塞”操作,而不會(huì)直接阻礙應(yīng)用的進(jìn)展。例如,在提供異步I/O讀取機(jī)制時(shí),專用線程可以在從socket或文件句柄中讀時(shí)阻塞。
            3. 操作完成:當(dāng)操作完成時(shí),應(yīng)用必須被通知到。特別是,專用線程必須將應(yīng)用特有的通知委托給Completion Dispatcher。這要求在線程間進(jìn)行另外的同步。
             
            8.7.2 實(shí)現(xiàn)完成分派器
             
            當(dāng)Completion Dispatcher從Asynchronous Operation Processor接收到操作完成通知時(shí),它會(huì)回調(diào)與應(yīng)用對(duì)象相關(guān)聯(lián)的Completion Handler。實(shí)現(xiàn)Completion Dispatcher涉及兩個(gè)問題:(1)實(shí)現(xiàn)回調(diào)以及(2)定義用于執(zhí)行回調(diào)的并發(fā)策略。
             
            8.7.2.1 實(shí)現(xiàn)回調(diào)
             
            Completion Dispatcher必須實(shí)現(xiàn)一種機(jī)制,Completion Handler通過它被調(diào)用。這要求Proactive Initiator在發(fā)起操作時(shí)指定一個(gè)回調(diào)。下面是常用的回調(diào)可選方案:
             
            回調(diào)類:Completion Handler輸出接口、讓Completion Dispatcher知道。當(dāng)操作完成時(shí),Completion Dispatcher回調(diào)此接口中的方法,并將已完成操作的有關(guān)信息傳遞給它(比如從網(wǎng)絡(luò)連接中讀取的字節(jié)數(shù))。
             
            函數(shù)指針:Completion Dispatcher通過回調(diào)函數(shù)指針來調(diào)用Completion Handler。該方法有效地打破了Completion Dispatcher和Completion Handler之間的知識(shí)依賴。這有兩個(gè)好處:
             
            1. Completion Handler不會(huì)被迫輸出特定的接口;以及
            2. 在Completion Dispatcher和Completion Handler之間不需要有編譯時(shí)依賴。
             
            會(huì)合點(diǎn):Proactive Initiator可以設(shè)立事件對(duì)象或條件變量,用作Completion Dispatcher和Completion Handler之間的會(huì)合點(diǎn)。這在Completion Handler是Proactive Initiator時(shí)最為常見。在Asynchronous Operation運(yùn)行至完成的同時(shí),Completion Handler處理其他的活動(dòng)。Completion Handler將在會(huì)合點(diǎn)周期性地檢查完成狀態(tài)。
             
            8.7.2.2 定義完成分派器并發(fā)策略
             
            當(dāng)操作完成時(shí),Asynchronous Operation Processor將會(huì)通知Completion Dispatcher。在這時(shí),Completion Dispatcher可以利用下面的并發(fā)策略中的一種來執(zhí)行應(yīng)用回調(diào):
             
            動(dòng)態(tài)線程分派:Completion Dispatcher可為每個(gè)Completion Handler動(dòng)態(tài)分配一個(gè)線程。動(dòng)態(tài)線程分派可通過大多數(shù)多線程操作系統(tǒng)來實(shí)現(xiàn)。在有些平臺(tái)上,由于創(chuàng)建和銷毀線程資源的開銷,這可能是所列出的Completion Dispatcher實(shí)現(xiàn)技術(shù)中最為低效的一種,
             
            后反應(yīng)式分派(Post-reactive dispatching ):Completion Dispatcher可以發(fā)信號(hào)給Proactive Initiation所設(shè)立的事件對(duì)象或條件變量。盡管輪詢和派生阻塞在事件對(duì)象上的子線程都是可選的方案,最為高效的后反應(yīng)式分派方法是將事件登記到Reactor。后反應(yīng)式分派可以通過POSIX實(shí)時(shí)環(huán)境中的aio_suspend和Win32環(huán)境中的WaitForMultipleObjects來實(shí)現(xiàn)。
             
            Call-through 分派:來自Asynchronous Operation Processor的線程控制可被Completion Dispatcher借用,以執(zhí)行Completion Handler。這種“周期偷取”策略可以通過減少空閑線程的影響范圍來提高性能。在一些老操作系統(tǒng)會(huì)將上下文切換到空閑線程、又只是從它們切換出去的情況下,這種方法有著收回“失去的”時(shí)間的巨大潛力。
            Call-through分派在Windows NT中可以使用ReadFileEx和WriteFileEx Win32函數(shù)來實(shí)現(xiàn)。例如,線程控制可以使用這些調(diào)用來等待信號(hào)量被置位。當(dāng)它等待時(shí),線程通知OS它進(jìn)入了一種稱為“可報(bào)警等待狀態(tài)”(alterable wait state)的特殊狀態(tài)。在這時(shí),OS可以占有對(duì)等待中的線程控制的棧和相關(guān)資源的控制,以執(zhí)行Completion Handler。
             
            線程池分派:由Completion Dispatcher擁有的線程池可被用于Completion Handler的執(zhí)行。在池中的每個(gè)線程控制已被動(dòng)態(tài)地分配到可用的CPU。線程池分派可通過Windows NT的I/O完成端口來實(shí)現(xiàn)。
             
            在考慮上面描述的Completion Dispatcher技術(shù)的適用性時(shí),考慮表8-1中所示的OS環(huán)境和物理硬件的可能組合? :

            線程模型
            系統(tǒng)類型
            單處理器
            多處理器
            單線程
            A
            B
            多線程
            C
            D
            表8-1 Completion Dispatcher并發(fā)策略
             
            如果你的OS只支持同步I/O,那就參見反應(yīng)堆模式[5]。但是,大多數(shù)現(xiàn)代操作系統(tǒng)都支持某種類型的異步I/O。
            在表8-1的A和B組合中,假定你不等待任何信號(hào)量或互斥體,后反應(yīng)方式的異步I/O很可能是最好的。否則,Call-through實(shí)現(xiàn)或許更能回應(yīng)你的問題。在C組合中,使用Call-through方法。在D組合中,使用線程池方法。在實(shí)踐中,系統(tǒng)化的經(jīng)驗(yàn)測(cè)量對(duì)于選擇最為合適的可選方案來說是必需的。
             
            8.7.3 實(shí)現(xiàn)完成處理器
             
            Completion Handler的實(shí)現(xiàn)帶來以下考慮。
             
            8.7.3.1 狀態(tài)完整性
             
            Completion Handler可能需要維護(hù)關(guān)于特定請(qǐng)求的狀態(tài)信息。例如,OS可以通知Web服務(wù)器,只有一部分文件已被寫到網(wǎng)絡(luò)通信端口。作為結(jié)果,Completion Handler可能需要重新發(fā)出請(qǐng)求,直到文件被完全寫出,或連接變得無效。因此,它必須知道原先指定的文件,還剩多少字節(jié)要寫,以及在前一個(gè)請(qǐng)求開始時(shí)文件指針的位置。
            沒有隱含的限制來阻止Proactive Initiator將多個(gè)Asynchronous Operation請(qǐng)求分配給單個(gè)Completion Handler。因此,Completion Handler必須在完成通知鏈中一一“系上”請(qǐng)求特有的狀態(tài)信息。為完成此工作,Completion Handler可以利用異步完成令牌(Asynchronous Completion Token)模式[8]。
             
            8.7.3.2 資源管理
             
            與在任何多線程環(huán)境中一樣,使用前攝器模式的Completion Handler還是要由它自己來確保對(duì)共享資源的訪問是線程安全的。但是,Completion Handler不能跨越多個(gè)完成通知持有共享資源。否則,就有發(fā)生“用餐哲學(xué)家問題”的危險(xiǎn)[11]。
            該問題在于一個(gè)合理的線程控制永久等待一個(gè)信號(hào)量被置位時(shí)所產(chǎn)生的死鎖。通過設(shè)想一個(gè)由一群哲學(xué)家出席的宴會(huì)可以演示這一問題。用餐者圍繞一個(gè)圓桌就座,在每個(gè)哲學(xué)家之間只有一支筷子。當(dāng)哲學(xué)家覺得饑餓時(shí),他必須獲取在他左邊和在他右邊的筷子才能用餐。一旦哲學(xué)家獲得一支筷子,不到吃飽他們就不會(huì)放下它。如果所有哲學(xué)家都拿起在他們右邊的筷子,就會(huì)發(fā)生死鎖,因?yàn)樗麄儗⒂肋h(yuǎn)也不可能拿到左邊的筷子。
             
            8.7.3.3 占先式策略(Preemptive Policy
             
            Completion Dispatcher類型決定在執(zhí)行時(shí)一個(gè)Completion Handler是否可占先。當(dāng)與動(dòng)態(tài)線程和線程池分派器相連時(shí),Completion Handler自然可占先。但是,當(dāng)與后反應(yīng)式Completion Dispatcher相連時(shí),Completion Handler并沒有對(duì)其他Completion Handler的占先權(quán)。當(dāng)由Call-through分派器驅(qū)動(dòng)時(shí),Completion Handler相對(duì)于在可報(bào)警等待狀態(tài)的線程控制也沒有占先權(quán)。
            一般而言,處理器不應(yīng)該執(zhí)行持續(xù)時(shí)間長(zhǎng)的同步操作,除非使用了多個(gè)完成線程,因?yàn)閼?yīng)用的總體響應(yīng)性將會(huì)被顯著地降低。這樣的危險(xiǎn)可以通過增強(qiáng)的編程訓(xùn)練來降低。例如,所有Completion Handler被要求用作Proactive Initiator,而不是去執(zhí)行同步操作。
             
            8.8 示例代碼
             
            這一部分顯示怎樣使用前攝器模式來開發(fā)Web服務(wù)器。該例子基于ACE構(gòu)架[4]中的前攝器實(shí)現(xiàn)。
            當(dāng)客戶連接到Web服務(wù)器時(shí),HTTP_Handler的open方法被調(diào)用。于是服務(wù)器就通過在Asynchronous Operation完成時(shí)回調(diào)的對(duì)象(在此例中是this指針)、用于傳輸數(shù)據(jù)的網(wǎng)絡(luò)連接,以及一旦操作完成時(shí)使用的Completion Dispatcher(proactor_)來初始化異步I/O對(duì)象。隨后讀操作異步地啟動(dòng),而服務(wù)器返回事件循環(huán)。
            當(dāng)Async read操作完成時(shí),分派器回調(diào)HTTP_Handler::handle_read_stream。如果有足夠的數(shù)據(jù),客戶請(qǐng)求就被解析。如果整個(gè)客戶請(qǐng)求還未完全到達(dá),另一個(gè)讀操作就會(huì)被異步地發(fā)起。
            在對(duì)GET請(qǐng)求的響應(yīng)中,服務(wù)器對(duì)所請(qǐng)求文件進(jìn)行內(nèi)存映射,并將文件數(shù)據(jù)異步地寫往客戶。當(dāng)寫操作完成時(shí),分派器回調(diào)HTTP_Handler::handle_write_stream,從而釋放動(dòng)態(tài)分配的資源。
            附錄中含有兩個(gè)其他的代碼實(shí)例,使用同步的線程模型和同步的(非阻塞)反應(yīng)式模型實(shí)現(xiàn)Web服務(wù)器。
             
            class HTTP_Handler
            : public Proactor::Event_Handler
            // = TITLE
            // Implements the HTTP protocol
            // (asynchronous version).
            //
            // = PATTERN PARTICIPANTS
            // Proactive Initiator = HTTP_Handler
            // Asynch Op = Network I/O
            // Asynch Op Processor = OS
            // Completion Dispatcher = Proactor
            // Completion Handler = HTPP_Handler
            {
            public:
            void open (Socket_Stream *client)
            {
            // Initialize state for request
            request_.state_ = INCOMPLETE;
             
            // Store reference to client.
            client_ = client;
             
            // Initialize asynch read stream
            stream_.open (*this, client_->handle (), proactor_);
             
            // Start read asynchronously.
            stream_.read (request_.buffer (),
            request_.buffer_size ());
            }
             
            // This is called by the Proactor
            // when the asynch read completes
            void handle_read_stream(u_long bytes_transferred)
            {
            if (request_.enough_data(bytes_transferred))
            parse_request ();
            else
            // Start reading asynchronously.
            stream_.read (request_.buffer (),
             
            request_.buffer_size ());
            }
             
            void parse_request (void)
            {
            // Switch on the HTTP command type.
            switch (request_.command ())
            {
            // Client is requesting a file.
            case HTTP_Request::GET:
            // Memory map the requested file.
            file_.map (request_.filename ());
             
            // Start writing asynchronously.
            stream_.write (file_.buffer (), file_.buffer_size ());
            break;
             
            // Client is storing a file
            // at the server.
            case HTTP_Request::PUT:
            // ...
            }
            }
             
            void handle_write_stream(u_long bytes_transferred)
            {
            if (file_.enough_data(bytes_transferred))
            // Success....
            else
            // Start another asynchronous write
            stream_.write (file_.buffer (), file_.buffer_size ());
            }
             
            private:
            // Set at initialization.
            Proactor *proactor_;
             
            // Memory-mapped file_;
            Mem_Map file_;
             
            // Socket endpoint.
            Socket_Stream *client_;
             
            // HTTP Request holder
            HTTP_Request request_;
             
            // Used for Asynch I/O
            Asynch_Stream stream_;
            };
             
            8.9 已知應(yīng)用
             
            下面是一些被廣泛記載的前攝器的使用:
             
            Windows NT中的I/O完成端口:Windows NT操作系統(tǒng)實(shí)現(xiàn)了前攝器模式。Windows NT支持多種Asynchronous Operation,比如接受新網(wǎng)絡(luò)連接、讀寫文件和socket,以及通過網(wǎng)絡(luò)連接傳輸文件。操作系統(tǒng)就是Asynchronous Operation Processor。操作結(jié)果在I/O完成端口(它扮演Completion Dispatcher的角色)上排隊(duì)。
             
            異步I/O操作的UNIX AIO族:在有些實(shí)時(shí)POSIX平臺(tái)上,前攝器模式是由aio API族[9]來實(shí)現(xiàn)的。這些OS特性非常類似于上面描述的Windows NT的特性。一個(gè)區(qū)別是UNIX信號(hào)可用于實(shí)現(xiàn)真正異步的Completion Dispatcher(Windows NT API不是真正異步的)。
             
            ACE ProactorACE自適配通信環(huán)境 [4]實(shí)現(xiàn)了前攝器組件,它封裝Windows NT上的I/O完成端口,以及POSIX平臺(tái)上的aio API。ACE前攝器抽象提供Windows NT所支持的標(biāo)準(zhǔn)C API的OO接口。這一實(shí)現(xiàn)的源碼可從ACE網(wǎng)站http://www.cs.wustl.edu/~schmidt/ACE.html獲取。
             
            Windows NT中的異步過程調(diào)用(Asynchronous Procedure Call):有些系統(tǒng)(比如Windows NT)支持異步過程調(diào)用(APC)。APC是在特定線程的上下文中異步執(zhí)行的函數(shù)。當(dāng)APC被排隊(duì)到線程時(shí),系統(tǒng)發(fā)出軟件中斷。下一次線程被調(diào)度時(shí),它將運(yùn)行該APC。操作系統(tǒng)所發(fā)出的APC被稱為內(nèi)核模式APC。應(yīng)用所發(fā)出的APC被稱為用戶模式APC。
             
            8.10 相關(guān)模式
             
            圖8-9演示與前攝器相關(guān)的模式。
             
             
            圖8-9 前攝器模式的相關(guān)模式
             
            異步完成令牌(ACT)模式[8]通常與前攝器模式結(jié)合使用。當(dāng)Asynchronous Operation完成時(shí),應(yīng)用可能需要比簡(jiǎn)單的通知更多的信息來適當(dāng)?shù)靥幚硎录.惒酵瓿闪钆颇J皆试S應(yīng)用將狀態(tài)高效地與Asynchronous Operation的完成相關(guān)聯(lián)。
            前攝器模式還與觀察者(Observer)模式[12](在其中,當(dāng)單個(gè)主題變動(dòng)時(shí),相關(guān)對(duì)象也會(huì)自動(dòng)更新)有關(guān)。在前攝器模式中,當(dāng)來自多個(gè)來源的事件發(fā)生時(shí),處理器被自動(dòng)地通知。一般而言,前攝器模式被用于異步地將多個(gè)輸入源多路分離給與它們相關(guān)聯(lián)的事件處理器,而觀察者通常僅與單個(gè)事件源相關(guān)聯(lián)。
            前攝器模式可被認(rèn)為是同步反應(yīng)堆模式[5]的一種異步的變體。反應(yīng)堆模式負(fù)責(zé)多個(gè)事件處理器的多路分離和分派;它們?cè)诳梢?i>同步地發(fā)起操作而不會(huì)阻塞時(shí)被觸發(fā)。相反,前攝器模式也支持多個(gè)事件處理器的多路分離和分派,但它們是被異步事件的完成觸發(fā)的。
            主動(dòng)對(duì)象(Active Object)模式[13]使方法執(zhí)行與方法調(diào)用去耦合。前攝器模式也是類似的,因?yàn)锳synchronous Operation Processor代表應(yīng)用的Proactive Initiator來執(zhí)行操作。就是說,兩種模式都可用于實(shí)現(xiàn)Asynchronous Operation。前攝器模式常常用于替代主動(dòng)對(duì)象模式,以使系統(tǒng)并發(fā)策略與線程模型去耦合。
            前攝器可被實(shí)現(xiàn)為單體(Singleton)[12]。這對(duì)于在異步應(yīng)用中,將事件多路分離和完成分派集中到單一的地方來說是有用的。
            責(zé)任鏈(Chain of Responsibility,COR)模式[12]使事件處理器與事件源去耦合。在Proactive Initiator與Completion Handler的隔離上,前攝器模式也是類似的。但是,在COR中,事件源預(yù)先不知道哪一個(gè)處理器將被執(zhí)行(如果有的話)。在前攝器中,Proactive Initiator完全知道目標(biāo)處理器。但是,通過建立一個(gè)Completion Handler(它是由外部工廠動(dòng)態(tài)配置的責(zé)任鏈的入口),這兩種模式可被結(jié)合在一起:。
             
            8.11 結(jié)束語
             
            前攝器模式包含了一種強(qiáng)大的設(shè)計(jì)范式,支持高性能并發(fā)應(yīng)用的高效而靈活的事件分派策略。前攝器模式提供并發(fā)執(zhí)行操作的性能助益,而又不強(qiáng)迫開發(fā)者使用同步多線程或反應(yīng)式編程。
             
            參考文獻(xiàn)
             
            [1] J. Hu, I. Pyarali, and D. C. Schmidt, “Measuring the Impact of Event Dispatching and Concurrency Models on Web Server Performance Over High-speed Networks,” in Proceedings of the 2nd Global Internet Conference, IEEE, November 1997.
            [2] J. Hu, I. Pyarali, and D. C. Schmidt, “Applying the Proactor Pattern to High-Performance Web Servers,” in Proceedings of the 10th International Conference on Parallel and Distributed Computing and Systems, IASTED, Oct. 1998.
            [3] J. C. Mogul, “The Case for Persistent-connection HTTP,” in Proceedings of ACMSIGCOMM ’95 Conference in Computer Communication Review, (Boston, MA, USA), pp. 299–314, ACM Press, August 1995.
            [4] D. C. Schmidt, “ACE: an Object-Oriented Framework for Developing Distributed Applications,” in Proceedings of the 6th USENIX C++ Technical Conference, (Cambridge, Massachusetts), USENIX Association, April 1994.
            [5] D. C. Schmidt, “Reactor: An Object Behavioral Pattern for Concurrent Event Demultiplexing and Event Handler Dispatching,” in Pattern Languages of Program Design (J. O. Coplien and D. C. Schmidt, eds.), pp. 529–545, Reading, MA: Addison-Wesley, 1995.
            [6] D. C. Schmidt, “Acceptor and Connector: Design Patterns for Initializing Communication Services,” in Pattern Languages of Program Design (R. Martin, F. Buschmann, and D. Riehle, eds.), Reading, MA: Addison-Wesley, 1997.
            [7] M. K. McKusick, K. Bostic, M. J. Karels, and J. S. Quarterman, The Design and Implementation of the 4.4BSD Operating System. Addison Wesley, 1996.
            [8] I. Pyarali, T. H. Harrison, and D. C. Schmidt, “Asynchronous Completion Token: an Object Behavioral Pattern for Efficient Asynchronous Event Handling,” in Pattern Languages of Program Design (R. Martin, F. Buschmann, and D. Riehle, eds.), Reading, MA: Addison-Wesley, 1997.
            [9] “Information Technology – Portable Operating System Interface (POSIX) – Part 1: System Application: Program Interface (API) [C Language],” 1995.
            [10] Microsoft Developers Studio, Version 4.2 - Software Development Kit, 1996.
            [11] E. W. Dijkstra, “Hierarchical Ordering of Sequential Processes,” Acta Informatica, vol. 1, no. 2, pp. 115–138, 1971.
            [12] E. Gamma, R. Helm, R. Johnson, and J. Vlissides, Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995.
            [13] R. G. Lavender and D. C. Schmidt, “Active Object: an Object Behavioral Pattern for Concurrent Programming,” in Proceedings of the 2nd Annual Conference on the Pattern Languages of Programs, (Monticello, Illinois), pp. 1–7, September 1995.
             
            附錄A 可選實(shí)現(xiàn)
             
            本附錄概述用于開發(fā)前攝器模式的可選實(shí)現(xiàn)的代碼。下面,我們檢查使用多線程的同步I/O和使用單線程的反應(yīng)式I/O。
             
            A.1 多個(gè)同步線程
             
            下面的代碼顯示怎樣使用線程池同步I/O來開發(fā)Web服務(wù)器。當(dāng)客戶連接到服務(wù)器時(shí),池中的一個(gè)線程接受連接,并調(diào)用HTTP_Handler中的open方法。隨后服務(wù)器同步地從網(wǎng)絡(luò)連接讀取請(qǐng)求。當(dāng)讀操作完成時(shí),客戶請(qǐng)求隨之被解析。在對(duì)GET請(qǐng)求的響應(yīng)中,服務(wù)器對(duì)所請(qǐng)求文件進(jìn)行內(nèi)存映射,并將文件數(shù)據(jù)同步地寫往客戶。注意阻塞I/O是怎樣使Web服務(wù)器能夠遵循2.2.1中所概述的步驟的。
             
            class HTTP_Handler
            // = TITLE
            // Implements the HTTP protocol
            // (synchronous threaded version).
            //
            // = DESCRIPTION
            // This class is called by a
            // thread in the Thread Pool.
            {
            public:
            void open (Socket_Stream *client)
            {
            HTTP_Request request;
             
            // Store reference to client.
            client_ = client;
             
            // Synchronously read the HTTP request
            // from the network connection and
            // parse it.
            client_->recv (request);
            parse_request (request);
            }
             
            void parse_request (HTTP_Request &request)
            {
            // Switch on the HTTP command type.
            switch (request.command ())
            {
            // Client is requesting a file.
            case HTTP_Request::GET:
            // Memory map the requested file.
            Mem_Map input_file;
            input_file.map (request.filename());
             
            // Synchronously send the file
            // to the client. Block until the
            // file is transferred.
            client_->send (input_file.data (),
            input_file.size ());
            break;
             
            // Client is storing a file at
            // the server.
            case HTTP_Request::PUT:
            // ...
            }
            }
             
            private:
            // Socket endpoint.
            Socket_Stream *client_;
             
            // ...
            };
             
            A.2 單線程反應(yīng)式事件分派
             
            下面的代碼顯示怎樣將反應(yīng)堆模式用于開發(fā)Web服務(wù)器。當(dāng)客戶連接到服務(wù)器時(shí),HTTP_Handler::open方法被調(diào)用。服務(wù)器登記I/O句柄和在網(wǎng)絡(luò)句柄“讀就緒“時(shí)回調(diào)的對(duì)象(在此例中是this指針)。然后服務(wù)器返回事件循環(huán)。
            當(dāng)請(qǐng)求數(shù)據(jù)到達(dá)服務(wù)器時(shí),reactor_回調(diào)HTTP_Handler::handle_input方法。客戶數(shù)據(jù)以非阻塞方式被讀取。如果有足夠的數(shù)據(jù),客戶請(qǐng)求就被解析。如果整個(gè)客戶請(qǐng)求還沒有到達(dá),應(yīng)用就返回反應(yīng)堆事件循環(huán)。
            在對(duì)GET請(qǐng)求的響應(yīng)中,服務(wù)器對(duì)所請(qǐng)求的文件進(jìn)行內(nèi)存映射;并在反應(yīng)堆上登記,以在網(wǎng)絡(luò)連接變?yōu)椤皩懢途w”時(shí)被通知。當(dāng)向連接寫入數(shù)據(jù)不會(huì)阻塞調(diào)用線程時(shí),reactor_就回調(diào)HTTP_Handler::handler_output方法。當(dāng)所有數(shù)據(jù)都已發(fā)送給客戶時(shí),網(wǎng)絡(luò)連接被關(guān)閉。
             
            class HTTP_Handler :
            public Reactor::Event_Handler
            // = TITLE
            // Implements the HTTP protocol
            // (synchronous reactive version).
            //
            // = DESCRIPTION
            // The Event_Handler base class
            // defines the hooks for
            // handle_input()/handle_output().
            //
            // = PATTERN PARTICIPANTS
            // Reactor = Reactor
            // Event Handler = HTTP_Handler
            {
            public:
            void open (Socket_Stream *client)
            {
            // Initialize state for request
            request_.state_ = INCOMPLETE;
             
            // Store reference to client.
            client_ = client;
             
            // Register with the reactor for reading.
            reactor_->register_handler
            (client_->handle (),
            this,
            Reactor::READ_MASK);
            }
             
            // This is called by the Reactor when
            // we can read from the client handle.
            void handle_input (void)
            {
            int result = 0;
             
            // Non-blocking read from the network
            // connection.
            do
            result = request_.recv (client_->handle ());
            while (result != SOCKET_ERROR && request_.state_ == INCOMPLETE);
             
            // No more progress possible,
            // blocking will occur
            if (request_.state_ == INCOMPLETE && errno == EWOULDBLOCK)
            reactor_->register_handler
            (client_->handle (),
            this,
            Reactor::READ_MASK);
            else
            // We now have the entire request
            parse_request ();
            }
             
            void parse_request (void)
            {
            // Switch on the HTTP command type.
            switch (request_.command ())
            {
            // Client is requesting a file.
            case HTTP_Request::GET:
             
            // Memory map the requested file.
            file_.map (request_.filename ());
             
            // Transfer the file using Reactive I/O.
            handle_output ();
            break;
             
            // Client is storing a file at
            // the server.
            case HTTP_Request::PUT:
            // ...
            }
            }
             
            void handle_output (void)
            {
            // Asynchronously send the file
            // to the client.
            if (client_->send (file_.data (),
            file_.size ())
            == SOCKET_ERROR
            && errno == EWOULDBLOCK)
            // Register with reactor...
            else
            // Close down and releas

            resources.

            handle_close ();
            }
             
            private:
            // Set at initialization.
            Reactor *reactor_;
             
            // Memory-mapped file_;
            Mem_Map file_;
             
            // Socket endpoint.
            Socket_Stream *client_;
             
            // HTTP Request holder.
            HTTP_Request request_;
            };

            本文轉(zhuǎn)載至ACE開發(fā)者網(wǎng)站 作者:Irfan Pyarali Tim Harrison
            posted on 2007-02-27 21:17 walkspeed 閱讀(3276) 評(píng)論(0)  編輯 收藏 引用 所屬分類: ACE Farmeworks

            <2007年2月>
            28293031123
            45678910
            11121314151617
            18192021222324
            25262728123
            45678910

            常用鏈接

            留言簿(4)

            隨筆分類(64)

            隨筆檔案(58)

            文章分類(3)

            文章檔案(3)

            相冊(cè)

            收藏夾(9)

            C++零碎

            好友

            搜索

            •  

            積分與排名

            • 積分 - 160953
            • 排名 - 163

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            人妻少妇久久中文字幕一区二区 | 久久精品国产精品国产精品污| 青青草原综合久久大伊人导航| 久久国产劲爆AV内射—百度| 国产精品99久久久久久人| 亚洲欧洲精品成人久久奇米网| 亚洲欧美久久久久9999 | 免费国产99久久久香蕉| 色综合久久88色综合天天| 久久成人精品视频| 中文字幕亚洲综合久久| 久久99精品久久久久久不卡| 久久精品国产第一区二区| 亚洲色欲久久久久综合网| 久久青青草原精品国产不卡| 综合网日日天干夜夜久久| 久久国产精品成人片免费| 久久久无码精品亚洲日韩软件| 精品久久久无码中文字幕| 国产一区二区精品久久凹凸| 三级片免费观看久久| 久久亚洲精品国产亚洲老地址| 无码八A片人妻少妇久久| 久久线看观看精品香蕉国产| 91精品国产91久久久久久| 久久综合色区| 久久天天躁狠狠躁夜夜躁2O2O| 久久国产一区二区| 日本精品久久久久影院日本| 久久亚洲国产精品成人AV秋霞| 久久国产免费观看精品3| 国产成人99久久亚洲综合精品| 午夜天堂av天堂久久久| 欧美日韩成人精品久久久免费看| 久久久久亚洲AV无码观看| 国产美女久久精品香蕉69| 亚洲欧美伊人久久综合一区二区| 久久精品a亚洲国产v高清不卡| 久久99久久无码毛片一区二区| 无码精品久久久久久人妻中字| 精品视频久久久久|