How to run boost asio resolver service on more threads?

偶尔善良 提交于 2019-12-01 11:23:51

What you need are two io_service ioService, because each one is run by a thread. By that I mean that you block the normal execution of a thread by calling io_service::run.

I think that the code itself is correct.

As noted in the documentation, Boost.Asio will create an an additional thread per io_service to emulate asynchronous host resolution on the first call to resolver::async_resolve().

Creating multiple io_service objects will allow for concurrent host resolution only when initiating asynchronous resolution operations on resolvers associated with different io_services. For example, the following code will not perform concurrent host resolution, as both resolvers use the same service:

boost::asio::io_service service1;
boost::asio::ip::udp::resolver resolver1(service1); // using service1
boost::asio::ip::udp::resolver resolver2(service1); // using service1
resolver1.async_resolve(...); 
resolver2.async_resolve(...);

On the other hand, the following will perform concurrent host resolution, as each resolver is using a different service:

boost::asio::io_service service1;
boost::asio::io_service service2;
boost::asio::ip::udp::resolver resolver1(service1); // using service1
boost::asio::ip::udp::resolver resolver2(service2); // using service2
resolver1.async_resolve(...); 
resolver2.async_resolve(...);

Assuming a resolver per io_service, to obtain concurrency, it becomes the applications responsibility to dispatch resolution operations to different resolvers. A simple work distribution strategy, such as round-robin, may suffice.

On the other hand, one can delegate this responsibility to an io_service, allowing it to distribute work that will emulate asynchronous host resolution in a similar manner to what Boost.Asio does internally. The synchronous resolver::resolve() member function performs work in the calling thread. Thus, an application could create an io_service that is serviced by a thread pool. When asynchronous host resolution needs to occur, a job is posted into the io_service that will create create a resolver and perform synchronous resolution, invoking the user handler with the results. Below is a complete basic example where a resolver class emulates asynchronous host resolution with a thread pool:

#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(io_service_))
  {
    // Create pool.
    for (std::size_t i = 0; i < pool_size; ++i)
      threads_.create_thread(
        boost::bind(&boost::asio::io_service::run, &io_service_));
  }

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

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

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(io_service_);
    typename resolver_type::iterator result = resolver.resolve(query, error);

    // Invoke user handler.
    handler(error, result);
  }

private:
  boost::asio::io_service io_service_;
  boost::optional<boost::asio::io_service::work> work_;
  boost::thread_group threads_;
};

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>);
}

And annotated output:

handle_resolve:
  Success
  127.0.0.1:12345   // localhost
handle_resolve:
  Service not found // bogus
handle_resolve:
  Success
  173.194.77.147:80 // google
handle_resolve:
  Success
  198.252.206.16:80 // stackoverflow
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!