1. 程式人生 > >從C++類成員函式作為回撥函式說起

從C++類成員函式作為回撥函式說起

在網路訊息處理中經常要用到回撥機制。

例如處理非同步網路操作的前攝器設計模式(Proactor),(可以參考 《C++ 網路程式設計 卷2》中關於ACE Proactor模式實現 )。

非同步的 Web 伺服器將這樣來利用前攝器模式:首先讓 Web 伺服器向 OS 發出非同步操作,並將回調方法登記到 Completion Dispatcher(完成分派器),後者將在操作完成時通知 Web 伺服器。於是 OS 代表 Web 伺服器執行操作,並隨即在一個周知的地方將結果排隊。Completion Dispatcher 負責使完成通知出隊,並執行適當的、含有應用特有的 Web 伺服器程式碼的回撥。

使用前攝器模式的主要優點是可以啟動多個併發操作,並可並行執行,而不要求應用必須擁有多個執行緒。操作被應用非同步地啟動,它們在 OS 的 I/O 子系統中執行直到完成。發起操作的執行緒現在可以服務 另外的請求了。

在ACE中,可以通過ACE_Proactor實現前攝器模式。實現方式如下。

1。建立服務處理器:

Proactor框架中服務處理器均派生自ACE_Service_Handler,它和Reactor框架的事件處理器非常類似。當發生IO操作完成事件時,會觸發相應的事件完成會調函式。

2。實現服務處理器IO操作

Proactor框架中所有的IO操作都由相應的非同步操作類來完成,這些非同步操作類都繼承自ACE_Asynch_Operation。常用的有以下幾種。

  1. ACE_Asynch_Read_Stream, 提供從TCP/IP socket連線中進行非同步讀操作.
  2. ACE_Asynch_Write_Stream, 提供從TCP/IP socket連線中進行非同步寫操作.

使用這些操作類的一般方式如下:

  1. 初始化
    將相關的操作註冊到服務處理器中,一般可通過呼叫其open方法實現。
  2. 發出IO操作
    發出非同步IO操作請求,該操作不會阻塞,具體的IO操作過程由作業系統非同步完成。
  3. IO操作完成回撥處理
    非同步IO操作完成後,OS會觸發服務處理器中的相應回撥函式,可通過該函式的ACE_Asynch_Result引數獲取相應的返回值。

3。使用聯結器或接受器和遠端進行連線

ACE為Proactor框架提供了兩個工廠類來建立TCP/IP連線。

  1. ACE_Asynch_Acceptor, 用於被動地建立連線
  2. ACE_Asynch_Connector 用於主動地建立連線

當遠端連線建立時,聯結器或接受器便會建立相應的服務處理器,從而可以實現服務處理。

4。啟動Proactor事件分發處理

啟動事件分發處理只需如下呼叫:

    while(true)
        ACE_Proactor::instance ()->handle_events ();
  

本次話題的重點是 ACE_Service_Handler 如何向 ACE_Proactor 註冊回撥函式。

ACE的做法是利用虛擬函式所得到的多型性質。

ACE_Service_Handler 作為網路事件處理器的基類,它通過虛擬函式規範了事件處理的介面函式。在客戶開發自己的網路程式時,需要寫自己的事件處理器,它必須從ACE_Service_Handler 繼承這些介面,並根據情況改寫它們。

網路程式在連線建立時將ACE_Service_Handler的派生類實體註冊到ACE_Proactor,在網路事件到達時,ACE_Proactor通過約定的介面可以將事件處理轉接到已註冊的ACE_Service_Handler的派生類實體。

上面實現的弊端是:

1. 使用繼承和虛擬函式增大了客戶程式與ACE框架的耦合性。客戶程式的事件處理器必須繼承自ACE_Service_Handler。眾所周知繼承是僅次於友元的耦合關係。

2. 用於使用繼承,客戶類的行為不完全可控,可能導致在資源管理時陷入兩難的境地。用過ACE寫過網路程式的都知道,ACE Proactor本身是一個半成熟的框架,一種情況是ACE_Service_Handler可能同時執行在ACE_Proactor所在的執行緒,以及另外一個傳送端控制執行緒上,在控制執行緒可能發起正常連線釋放,也有可能在事件處理中檢測到網路故障而釋放,這兩種釋放可能在兩個不同的執行緒中,要做到資源沒有洩露並非易事。

3. 虛擬函式的執行開銷。

那麼是否有一種回撥機制使得事件處理器(Event Handler)不必從一個限定的基類派生,而是可以將自己符合介面定義的任一成員函式作為回撥函式直接註冊到完成分派器(Completion Dispatcher)。

當然,時代在進步,新的技術為我們提供了完善的解決方案。從《Modern C++ Design》中的探索實踐。到boost fuction 和boost bind的標準化方案,以上想法已經被很多庫實現。

下面介紹一個例子,用boost function 和boost bind 來將類成員函式用於回撥機制。

已知應用:boost asio使用更現代的C++的方法實現前攝式設計模式(Proactor)。可以將類成員函式通過boost bind適配為完成事件處理函式。從而解除了對公共基類的依賴。

如下是利用 boost asio 寫的一個udp的例子。在這個例子中假設我們的角色是一個udp客戶端。實際上從協議上看upd通訊的雙方是對等的。沒有客戶端與伺服器之分。