ACE將網路編程進行了模式化,以便你不必每次都重複相同的代碼。
網路編程需要處理的事情多括中斷,並發,多線程等,程式格式相對固定,但是健壯的網路程式則相對複雜。為了處理這些情形,ACE內建了幾個網路編程的模式。
最基本的模式當然是直接使用sock進行單客戶單伺服器單線程的一對一模型,這種模式相對簡單,也和ACE關係不大,但是這樣編寫的程式不能處理並發的情況,可用性很差或者說基本不具有可用性。
最簡單的處理並發但是卻使用單線程的架構在ACE中稱為Reactor架構,在這種架構下,Reactor扮演了協調員的角色,應用程式編製者需要首先寫好各種各樣的事件處理常式,然後在Reactor中進行登記,Reactor以阻塞的方式同時監視所有可能發生的事件,並且在相應的事件發生的時候調用對應的處理過程。這種架構解決了在單線程的前提下解決了並發,但是存在一定的問題,如果某個事件執行過程過長,則可能導致Reactor漏過某些事件。
另外一種單線程處理並發的模式稱為非同步I/O的Proactor模式,這種模式和前面介紹的Reactor模式其實區別不大,唯一的區別之處在於,Server類在對從網路上收到的訊息進行處理的時候,後者並不直接讓處理器處理收到的訊息,而是首先將訊息轉換為一個訊息塊結構(ACE_Message_Block,通過this->reader_.read函數),然後再讓相應的處理函數處理已經接收好的訊息塊結構。
比較一下Reactor架構和Proactor架構,前者的執行流程是: 監視事件->呼叫事件處理過程->繼續監視事件。 後者的執行流程是: 監視事件->產生訊息->處理訊息->釋放訊息->繼續監視事件。這兩種不同的架構在引入各自的多線程概念以後,就衍生出不同的多線程架構。
前面說過,使用多線程進行網路編程也有兩種架構,半同步/半非同步架構和領導者/跟隨者架構。前者對應的是Proactor架構,後者對應的是Reactor架構。所謂半同步或者半非同步架構,執行的流程是:主線程負責 監視事件->產生訊息->放入訊息佇列->監視事件,背景工作執行緒則負責從擷取訊息->處理訊息->從訊息佇列擷取另外一個訊息。 這種架構的優勢在於,由於構造訊息並且將其放入訊息佇列的時間是可以控制的,因此,可以很好的處理網路峰值的情況,即使出現很高的峰值,也不會造成訊息的遺漏,但是由於訊息存在一個入隊列,出隊列的過程,因此效能相較另外一種模型,理論上更差。
後者則是一種相對更複雜的模型,線上程池中只有一個線程是領導者線程,其他為跟隨者線程,領導者線程監視事件,在事情發生的時候,首先尋找另外一個線程變為領導者,然後自己再處理事件,處理完成以後,首先嘗試再次成為領導者,如果嘗試失敗(另外一個線程已經成為領導者),則自己變成跟隨者。 這種模型基於Reactor模型,沒有訊息佇列的概念,由於不存在出入隊列的過程,效能相對前者理論上更好。但是如果存在很高的網路蜂擁,則可能由於所有的線程都在處理各自的事件,導致沒有領導者可用,出現資料丟失的可能。
在這兩種多執行緒模式中都存線上程池的使用。在半同步/半非同步模型中,工作者線程可能為一個工作者線程池。訊息佇列的線程同步的工作已經由ACE架構自動完成,是不是工作者線程越多越好呢? 答案是否定的。 多線程可以提高客戶的響應速度。比如同時有A,B兩個用戶端先後發起兩個請求,A請求完成的時間較長,B請求則可以很快完成,如果只有一個背景工作執行緒,那麼B需要等待A請求完成以後才能收到自己的響應,對於A來說,它本來就不期待自己的請求很快被完成,實際的執行情況會是,A在期待的時間內收到響應,B則使用了A的時間才收到自己的響應,B的客戶滿意度就會很差。 如果使用多線程,A會延遲一點點收到自己的響應,而B也可以在合理的時間內收到自己的響應。 但是由於多線程有自己的開銷,就整個系統來說,單背景工作執行緒執行A和B的總時間回比多背景工作執行緒執行AB任務的總時間要短。
對於領導者/跟隨者模型中,必然存在一個對等的線程池,線程池的數目取決於系統能夠承受的數目,單就對於模型本身來說,線程池的線程數目越大,能夠承受的網路蜂擁的極限值也越大。 但是如果執行每個請求的時間都很短,則系統中存在大量永遠也用不到的線程,浪費了系統的資源。
如果使用多處理器的系統,應用程式必然能夠從多線程(背景工作執行緒和跟隨者線程)結構中收益