How to run boost asio resolver service on more threads?

后端 未结 2 612
离开以前
离开以前 2021-01-15 05:16

I am using boost::asio::ip::udp::resolver in an SNMPV2 implementation to determine wheather a host is reachable or not.

using Resolver = boost::asio::ip::udp         


        
相关标签:
2条回答
  • 2021-01-15 05:39

    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
    
    0 讨论(0)
  • 2021-01-15 05: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.

    0 讨论(0)
提交回复
热议问题