[Boost] ASIO學習筆記:初探io_service

posted in: ASIO, boost, C/C++程式設計 | 0

ASIO的實現,其實就是基於Proactor pattern,在這裡我們先不對這個pattern做探討,等以後有機會再來談

想像io_service即是os跟我們程式之間的橋梁,
當執行io_service.run()的時候,便是將系統轉移到os上,
並block住原本的程式,直至所有的工作佇列完成,其是一種synchronous 模式。

只是,有時候我們並不希望程式在完成工作之後就結束,而是繼續等待是否有其他工作傳入,這時候我們就可以加入
boost::asio::io_service::work work( io_service );,以便讓系統能夠持續的運行。

完整程式碼如下:

#include <boost/asio.hpp>
#include <iostream>
int main( int argc, char * argv[] ){        
        boost::asio::io_service io_service;      
        boost::asio::io_service::work work( io_service );       
        io_service.run();        
        std::cout < < "這行永遠到不了" << std::endl;       
        return 0;
}

為什麼會這樣呢?那是因為work object 提供一個work給 io_service object這件事,本身就是一個會被加入到Completion Event Queue裡面的工作。(Completion Event Queue是一組由linked list組成的function object,在boost裡面通常是用Bind函數實現。)

雖然這種做法可以確保所有非同步的工作都被完成,但是,其缺點就是會讓我們的程式陷入無窮的等待中而無法進行別的事情。那麼是不是有一個方法,可以讓我們在執行這些非同步的工作之餘也去處理其他的程序呢?當然,作為一個完整用來處理非同步問題的函式庫,asio當然會提供這樣的一個方法。

在io_service的成員函數中,我們可以發現有以下幾個方法:
poll():處理所有已被加入到工作佇列中的ready handler
poll_one(): 只處理『一個』已被加入工作佇列中的ready handler

所謂的ready handler,指的就是已完成非同步的操作及等待(例如:data已藉由網路傳輸完成),被加入Completion Event Queue的handler

這些function用來定義當非同步操作等待完成後所該進行的工作

在windowsNT、XP及2000之後的系列上 Completion Event Queue等同於I/O completion port。而每個io_service instance都會擁有一個I/O completion port。

依程式需求選擇poll()、poll_one(),我們可以改寫上一篇的程式碼,來達成asynchronous的模式:

#include <boost/asio.hpp> 
#include <iostream> 
 using namespace std;
void handler1(const boost::system::error_code &ec) 
{ 
  std::cout < < "5 s." << std::endl; 
  system("pause");
} 
 
void handler2(const boost::system::error_code &ec) 
{ 
  std::cout << "10 s." << std::endl; 
 
   system("pause");
} 
 
int main() 
{ 
    boost::asio::io_service io_service; 
    boost::asio::deadline_timer timer1(io_service, boost::posix_time::seconds(5)); 
    timer1.async_wait(handler1); 
    boost::asio::deadline_timer timer2(io_service, boost::posix_time::seconds(10)); 
    timer2.async_wait(handler2); 
    int i = 0;
    while(true){
      io_service.poll();
      cout << "counter:" << i << endl;
      i++;
    }
    system("pause");
}

使用run()、run_one()、poll()以及poll_one()的分別:
run():等待並將工作佇列中的任務全部處理完再繼續執行 (Blocking 模式)
poll():去看Completion Event Queue中是否有待處理的handler,有的話就全部執行,沒有的話就繼續執行我們的主程序(non-Blocking 模式)
run_one():等待並確實的執行『一個』Completion Event Queue中的handler
poll_one():只執行一個Completion Event Queue中的handler

在ASIO中是以先進先出法來取得Completion Event Queue裡面的handler

Leave a Reply

Your email address will not be published. Required fields are marked *