剖析asio中的proactor模式(二)



http://www.cnblogs.com/qicosmos/p/3841026.html

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

  1. 創建IOCP對象;
  2. 創建io object對象;
  3. 將io object IOCP對象綁定;
    4.進行異步調用;
  4. 創建線程或者由線程池等待完成事件的到來;

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

asio::io_service io_service; 
tcp::socket socket(io_service);
boost::asio::async_connect(socket, server_address, connect_handler); //端口綁定、異步操作交由OS
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模式中的異步事件處理和②i啟動事件循環,調度異步事件多路分離器將異步事件回調到應用層。

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



注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com