程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> (原創)撥開迷霧見月明-剖析asio中的proactor模式(二),asioproactor

(原創)撥開迷霧見月明-剖析asio中的proactor模式(二),asioproactor

編輯:C++入門知識

(原創)撥開迷霧見月明-剖析asio中的proactor模式(二),asioproactor


  在上一篇博文中我們提到異步請求是從上層開始,一層一層轉發到最下面的服務層的對象win_iocp_socket_service,由它將請求轉發到操作系統(調用windows api),操作系統處理完異步請求之後又是如何返回給應用程序的呢,這裡是通過iocp(完成端口)來實現的。讓我們先來簡要的看看iocp的基本步驟:

  asio實際上也是按照這個步驟去做的,再回頭看看上一節中的那個簡單的例子:

asio::io_service io_service; 
tcp::socket socket(io_service); 
boost::asio::async_connect(socket, server_address, connect_handler); 
io_service.run(); 

  第一行中的io_service對象是asio的核心,它其實封裝了iocp,創建一個io_service實際上就是創建了一個iocp對象win_iocp_io_service,因此後面所有的io object的創建都要引用這個io_service,目的是共用這個iocp對象。第二行創建了socket對象,它引用了第一行創建的iocp對象;第三行實際上是將異步請求層層轉發到最下面的服務層win_iocp_socket_service對象,最終交給操作系統。通過它的名字就知道它與iocp相關,因為發起異步操作之前,它先要將io object對象與完成端口綁定,以便後面的完成事件會發到指定的完成端口。

  綁定io object和iocp對象的具體過程是這樣的:async_connect內部會先調用base_xxx模板層的base_socket<tcp>的open方法,base_socket<tcp>又會調用服務層的服務對象stream_socket_service<tcp>的open方法,stream_socket_service<tcp>又調用最下面的服務對象win_iocp_socket_service的open方法,win_iocp_socket_service對象又委托io object對象引用的io_service對象(實際上是win_iocp_io_service)的do_open方法,在do_open方法中會調用register_handler方法,在該方法中會調用CreateIoCompletionPort將io object和iocp對象綁定起來。

  io object和iocp對象綁定之後,win_iocp_socket_service會調用操作系統的api,發起異步操作。

  再看第四行:io_service.run();

  io_service::run()又是委托win_iocp_io_service::run()來實現的,讓我們來看看run的內部實現:

size_t win_iocp_io_service::run(boost::system::error_code& ec)
{
 if (::InterlockedExchangeAdd(&outstanding_work_, 0) == 0)
 {
   stop();
   ec = boost::system::error_code();
   return 0;
 }
 
 win_iocp_thread_info this_thread;
 thread_call_stack::context ctx(this, this_thread);
 
 size_t n = 0;
  while (do_one(true, ec))
   if (n != (std::numeric_limits<size_t>::max)())
     ++n;
 return n;
}

  run()首先檢查是否有需要處理的操作,如果沒有,函數退出;win_iocp_io_service使用outstanding_work_來記錄當前需要處理的任務數。如果該數值不為0,則委托do_one函數繼續處理。do_one()內部會調用GetQueuedCompletionStatus()函數,該函數會阻塞等待異步事件的完成,當異步事件完成時,就回調到應用層的完成事件處理函數,因為發起異步操作時已經將io object和完成端口綁定了,所以iocp能將異步完成事件回調到對應的應用層的完成處理函數中。

  至此,asio中一個異步操作的過程就完成了。在了解了這些內部實現細節之後,我們再來看看boost官網上給出的一個asio中proactor模式的一張圖。

  這張圖和上一篇博文中Proactor模式的圖幾乎是一樣的,我們根據這張圖再結合前面的分析,就能從細節中還原出asio中的Proactor模式了。下面我們來看看上圖中的這些對象分別是asio中的哪些對象:

  • Initiator:對應用戶調用asio的代碼;
  • Asynchronous Operation Processor:異步操作處理器,他負責執行異步操作,並在操作完成後,把完成事件投放到完成事件隊列上。stream_socket_service類就是一個這樣的處理器,因為從tcp::socket發送的異步操作都是由其完成處理的,它最終是由底層的服務對象win_iocp_socket_service完成的,win_iocp_socket_service負責綁定io object和io_service對象和調用操作系統api發起異步操作。從高層的角度看,asio的stream_socket_service成為了Proactor中的異步操作處理器。
  • Asynchronous Operation:定義的一系列異步操作,對應到Windows平台,諸如AcceptEx,WSASend,WSARecv等函數。在asio中,這些函數封裝在win_iocp_socket_service,resolver_service類中。[1]
  • Completion Handler:用戶層完成事件處理器,由用戶創建,一般是通過bind或者lambda表達式定義。
  • Completion Event Queue:完成事件隊列,存儲由異步操作處理器發送過來的完成事件,當異步事件多路分離器將其中一個事件取走之後,該事件從隊列中刪除;在Windows上,asio的完成事件隊列由操作系統負責管理;
  • Asynchronous Event Demultiplexer:異步事件多路分離器,他的作用就是在完成事件隊列上等待,一旦有事件到來,他就把該事件返回給調用者。在Windows上,這一功能也是由操作系統完成的,具體來說,是由GetQueuedCompletionStatus完成的,而該函數是由do_one()調用的,因此,從高層的角度來看,這個分離器,也是由io_service負責的。[2]
  • Proactor,前攝器,負責調度異步事件多路分離器去干活,並在異步操作完成時,調度所對應的Completion Handler。在asio中,這部分由io_service來做,具體Windows就是win_iocp_io_service。[3]

  從上面的分析可以看到,asoi中的Proactor模式已經很清晰了,io_service在asio中處於核心地位,不僅僅是對應了一個完成端口對象,還參與了Proactor模式中的異步事件處理和啟動事件循環,調度異步事件多路分離器將異步事件回調到應用層。

  再來做一個小結:io object負責發起異步操作,發起異步操作的過程中,會委托stream_socket_service將異步操作轉發到下面的服務層,最終轉發到操作系統。io object創建時需要引用io_service,以便在後面綁定完成端口,同時還要提供完成事件處理函數,以便在異步操作完成後處理完成事件。io_service負責啟動事件循環,等待異步事件的完成並將異步操作的結果回發到用戶定義的完成事件處理函數中。


[1] [2] [3] http://blog.csdn.net/henan_lujun/article/details/8965044

如果你覺得這篇文章對你有用,可以點一下推薦,謝謝。

c++11 boost技術交流群:296561497,歡迎大家來交流技術。  



  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved