c++ - 将 boost::asio 集成到基于文件描述符的事件循环中(选择/轮询)

标签 c++ boost boost-asio

如果我想将 boost::asio 中的内容集成到基于文件描述符(选择/轮询)的事件循环中,我该如何实现?其他具有异步功能的库提供了一个文件描述符,一旦有工作要做,它就会变得可读,这样你就可以将它集成到事件循环的选择/轮询中,并让它调用库的处理回调(就像单次事件处理)。

一个很好的例子是线程池中的异步名称解析器,如 this question 中所讨论的那样.

最佳答案

基于 this answer 中的示例我想出了这个使用通用处理程序的解决方案,该处理程序写入唤醒管道,然后将处理程序调用发布到另一个 io_service。管道的读取端可用于基于文件描述符的事件循环,并从那里调用回调 run_handler(),这会清除管道并在主线程中运行挂起的处理程序。

#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/optional.hpp>
#include <boost/thread.hpp>

/// @brief Type used to emulate asynchronous host resolution with a 
///        dedicated thread pool.
class resolver {
public:
  resolver(const std::size_t pool_size)
    : work_(boost::ref(resolver_service_)) {
    // Create wake-up pipe
    pipe(pipe_);
    fcntl(pipe_[0], F_SETFL, O_NONBLOCK);
    // Create pool.
    for (std::size_t i = 0; i < pool_size; ++i)
      threads_.create_thread(boost::bind(&boost::asio::io_service::run,
        &resolver_service_));
  }

  ~resolver() {
    work_ = boost::none;
    threads_.join_all();
  }

  template <typename QueryOrEndpoint, typename Handler>
  void async_resolve(QueryOrEndpoint query, Handler handler) {
    resolver_service_.post(boost::bind(
        &resolver::do_async_resolve<QueryOrEndpoint, Handler>, this,
        query, handler));
  }

  // callback for eventloop in main thread
  void run_handler() {
    char c;
    // clear wake-up pipe
    while (read(pipe_[0], &c, 1) > 0);
    // run handler posted from resolver threads
    handler_service_.poll();
    handler_service_.reset();
  }

  // get read end of wake up pipe for polling in eventloop
  int fd() {
    return pipe_[0]; 
  }

private:
  /// @brief Resolve address and invoke continuation handler.
  template <typename QueryOrEndpoint, typename Handler>
  void do_async_resolve(const QueryOrEndpoint& query, Handler handler) {
    typedef typename QueryOrEndpoint::protocol_type protocol_type;
    typedef typename protocol_type::resolver        resolver_type;

    // Resolve synchronously, as synchronous resolution will perform work
    // in the calling thread.  Thus, it will not use Boost.Asio's internal
    // thread that is used for asynchronous resolution.
    boost::system::error_code error;
    resolver_type resolver(resolver_service_);
    typename resolver_type::iterator result = resolver.resolve(query, error);

    // post handler callback to service running in main thread
    handler_service_.post(boost::bind(handler, error, result));
    // wake up eventloop in main thread
    write(pipe_[1], "*", 1);
  }

private:
  boost::asio::io_service resolver_service_;
  boost::asio::io_service handler_service_;
  boost::optional<boost::asio::io_service::work> work_;
  boost::thread_group threads_;
  int pipe_[2];
};

template <typename ProtocolType>
void handle_resolve(
    const boost::system::error_code& error,
    typename ProtocolType::resolver::iterator iterator) {
  std::stringstream stream;
  stream << "handle_resolve:\n"
            "  " << error.message() << "\n";
  if (!error)
    stream << "  " << iterator->endpoint() << "\n";

  std::cout << stream.str();
  std::cout.flush();
}

int main() {
  // Resolver will emulate asynchronous host resolution with a pool of 5
  // threads.
  resolver resolver(5);

  namespace ip = boost::asio::ip;
  resolver.async_resolve( 
      ip::udp::resolver::query("localhost", "12345"),
      &handle_resolve<ip::udp>);
  resolver.async_resolve(
      ip::tcp::resolver::query("www.google.com", "80"),
      &handle_resolve<ip::tcp>);
  resolver.async_resolve(
      ip::udp::resolver::query("www.stackoverflow.com", "80"),
      &handle_resolve<ip::udp>);
  resolver.async_resolve(
      ip::icmp::resolver::query("some.other.address", "54321"),
      &handle_resolve<ip::icmp>);

  pollfd fds;
  fds.fd = resolver.fd();
  fds.events = POLLIN;

  // simple eventloop
  while (true) {
    if (poll(&fds, 1, 2000)) // waiting for wakeup call
      resolver.run_handler(); // call resolve handler
    else
      break;
  }
}

关于c++ - 将 boost::asio 集成到基于文件描述符的事件循环中(选择/轮询),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24449936/

相关文章:

c++ - 需要一些建议以使代码多线程

c++ - fopen/fclose 性能

c++ - 类模板的可变参数构造函数模板的特化

c++ - 如何在 RHEL 上为新的 Boost 版本构建/部署 RPM?

c++ - 如何使用boost C++解析8位十六进制数和字符串?

c++ - 如何确保所有 async_handler 在 boost::asio 停止之前完成?

c++ - 串行端口奇偶校验在 boost asio 中失败

c++ - 如何在等待时取消 `boost::asio::read` 操作

c++ - 我的 Tic Tac Toe 游戏代码中的一个问题

c++ - 将 boost::multiprecision 数据类型写入二进制文件