io_service对象的工作机制

时间:2018-02-06 17:17:16

标签: boost boost-asio

我对io_services对象的工作机制感到困惑。我的理解是有一个队列与io_service对象关联,如果任何异步调用将在队列中添加一个项目,当调用io_service.run_once时,将运行一个异步调用并从队列中出队。如果队列为空,则在添加新调用之前,io_service.run_one将不执行任何操作。我在boost示例中组织了一些代码,但似乎我的理解是错误的。

#include <boost/asio/connect.hpp>
#include <boost/asio/io_service.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/system/system_error.hpp>
#include <boost/asio/write.hpp>
#include <cstdlib>
#include <iostream>
#include <string>
#include <boost/bind.hpp>
using boost::asio::deadline_timer;
using boost::asio::ip::tcp;

class client
{
public:
  client()
    : socket_(io_service_)
  {
  }

  void connect_handler(const boost::system::error_code& error,boost::system::error_code *er)
  {
      std::cerr<<"connect handler"<<std::endl;
      *er = error;
      std::cerr<<error<<std::endl;
  }

  void connect(const std::string& host, const std::string& service)
  {
    tcp::resolver::query query(host, service);
    tcp::resolver::iterator iter = tcp::resolver(io_service_).resolve(query);
    std::cerr<<"connect start"<<std::endl;

    boost::system::error_code ec = boost::asio::error::would_block;
    boost::asio::async_connect(socket_, iter, bind(&client::connect_handler,this,_1,&ec));

    do
    {io_service_.run_one();
    }while (ec == boost::asio::error::would_block);
    //io_service_.reset();  // The write async will be stuck without this reset call.
    std::cerr<<"connect done"<<std::endl;
    if (ec || !socket_.is_open())
      throw boost::system::system_error(
          ec ? ec : boost::asio::error::operation_aborted);
  }

  void write_handler(const boost::system::error_code& error, std::size_t size,boost::system::error_code* er )
  {
      std::cerr<<"write handler "<<std::endl;
      *er=error;
      std::cerr<<error<<std::endl;
  }

  void write_line(const std::string& line)
  {
    std::cerr<<"write start"<<std::endl;
    std::string data = line + "\n";
    boost::system::error_code ec = boost::asio::error::would_block;    
    boost::asio::async_write(socket_, boost::asio::buffer(data), bind(&client::write_handler,this,_1,_2,&ec));
    do
    {
        io_service_.run_one();     
    }while (ec == boost::asio::error::would_block);
    std::cerr<<"write done";
    if (ec)
      throw boost::system::system_error(ec);
  }

private:
  boost::asio::io_service io_service_;
  tcp::socket socket_;
};

int main()
{
  try
  {
    client c,d;
    c.connect("172.217.6.36", "80");// google IP.
    c.write_line("example");
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}

我的理解是:

   Start
     | 
async_connect ----> add one item in io_service queue 
     |
     |
io_serivce.run_one() ------> dequeue the async_connect call back from io_serivce queue 
     |
     |
connect_handler --------> connect_handler called change the ec value
     |
     |
async_write ----------> add one item in io_service queue.
     |
     |
io_service.run_one()------------>dequeue the async_write call back from io_serivce queue  
     |
     |
write_handler()----------------->write handler called and change the ec value
     |
   Done

但现实是

Start
     | 
async_connect ----> add one item in io_service queue 
     |
     |
io_serivce.run_one() ------> dequeue the async_connect call back from io_serivce queue 
     |
     |
connect_handler --------> connect_handler called change the ec value
     |
     |
async_write ----------> add one item in io_service queue.
     |
     |
io_service.run_one()------------>stuck here in the while loop forever, the async_write handler is never be called the ec is never be changed. 
Sehe告诉我,需要在this中调用io_service.reset,我不明白为什么需要调用io_service.reset?原始another post没有使用此调用,它可以正常工作。重置调用有效:

   Start
     | 
async_connect ----> add one item in io_service queue 
     |
     |
io_serivce.run_one() ------> dequeue the async_connect call back from io_serivce queue 
     |
     |
connect_handler --------> connect_handler called change the ec value
     |
     |
io_service.reset() --------> reset the io service.
     |
     |
async_write ----------> add one item in io_service queue.
     |
     |
io_service.run_one()------------>dequeue the async_write call back from io_serivce queue  
     |
     |
write_handler()----------------->write handler called and change the ec value
     |
   Done

1 个答案:

答案 0 :(得分:1)

原始样本使用截止时间计时器,该计时器位于async_waits的连续链中。这意味着io_service永远不会失去工作。

就是这样。整个区别。如果您让服务失效,run_*将会返回,您需要先致电reset(),然后才能再次使用io_service

另见Why must io_service::reset() be called?

对于上下文中的早期答案boost socket example stuck in while loop,我提供了几种更好的方法来使用同步调用或使用异步调用。