Darwin Streaming server 的 Task 類
Darwin Streaming Server 是一個開放源代碼的streaming server,對于streaming server的編程和軟件結構有著一定的參考價值,它是使用C++寫的,其中的并發模式的核心就是Task類,下面寫一下我的理解:
多任務的程序常常采用線程+同步阻塞IO的模式, 每個線程/進程服務于一個client,使用阻塞式的IO:
這種模式對于交互式的長連接應用也是常見的選擇(比如Telnet)。好處是實現極其簡單,容易嵌入復雜的交互邏輯。Apache、ftpd 等都是這種工作模式。但是這種策略很能難足高性能程序的需求。
在handle大量用戶的情況下,為了避免創建過多的線程導致context switch開銷,常常采用select I/O復用的方法(你可能說select過時了,不過Darwin QTSS就是用的這個):
上面是經典的select IO復用的過程, 以上的過程可以由3步來描述:
-
應用注冊事件
-
事件觸發通知應用
-
應用運行處理事件
注意在SELECT THREAD有3個任務:接受事件注冊,等待事件觸發,驅動SESSION處理事件,這些任務可以分解為不同的角色,我們可以在這種模式里定義4種角色:
1. EventHandler : EventHandler 向 EventGenerator注冊事件,并對注冊事件進行處理
2.EventGenerator :EventGenerator接受事件注冊,當事件觸發的時候,通知 EventHandler
3.EventHandler Driver : EventHandler Driver 驅動EventHandler 的運行,當它發現有EventHandler 受到事件觸發以后,調度運行 EventHandler 的事件處理函數。一個EventHandler Driver可以驅動多個EventHandler。多個EventHandler Driver可以組成 Pool 進行驅動EventHandler。
4. Event Driver : Event Driver 驅動 EventGenerator 的事件觸發。EventGenerator本身不包含執行線程,需要Event Driver的驅動。多個EventGenerator 在 Event Driver 中等待事件,當事件發生時,Event Driver調度 EventGenerator的來觸發事件。
下面就是Darwin對各個對象和上述角色的對應:
-
Task 就是對EventHandler的對象化封裝。每個Task對象有兩個主要的方法:Signal和Run。當服務器希望發送一個事件給某個Task對象時,就會調用Signal()方法;而Run()方法是在Task對象獲得處理該事件的時間片后運行的,服務器中的大部分工作都是在不同Task對象的Run()函數中進行的。每個Task對象的目標就是利用很小的且不會阻塞的時間片完成服務器指定某個工作。應用可以通過繼承Task 并重寫Run()方法實現自己的任務。
-
EventContext 對應EventGenerator的角色,事件的觸發者,當事件發生時,調用Task::signal().
-
TaskThread 對應EventHandler Driver的角色。任務的驅動線程,對一個或者多個Task進行調度,通過調用 Task::run() 處理事件
-
EventThread對應Event Driver的角色。EventContext的驅動線程,可以處理多個EventContext, 發生事件時調用EventContext::process_event(),后者將調用Task::Signal()
流程:
-
Client或者 Task的子類向Event Context注冊事件。
-
Event Context將事件放入EventThread的Pool內。
-
EventThread 調用select 等待多個事件中任一個觸發。
-
事件觸發以后,EventThread調用 Event Context::process_event()。
-
調用 Task::signal()。
-
Task::signal()將task放入TaskThread的隊列。
-
TaskThread調度相應的Task, 執行其Run()方法。