【问题标题】:Does the method post() of the object boost::asio::io_service uses the boost::coroutines for short tasks?对象 boost::asio::io_service 的方法 post() 是否将 boost::coroutines 用于短任务?
【发布时间】:2013-06-23 13:27:11
【问题描述】:

对象 boost::asio::io_service 的方法 post() 方法是否使用 boost::coroutines 来执行在处理程序中执行的短任务队列?这可以节省使用线程时用于同步的资源,但无法将任务移动到另一个线程。还是没有意义?

【问题讨论】:

  • Boost.Coroutine 在 Boost 1.53 中被接受,Boost.Asio - 在 1.35 中。显然,后者不依赖于前者。
  • @Igor R. boost::asio 在 1.35 和 1.53 版本之间没有区别吗?为什么不在新版本中利用旧库中的新机会?
  • @Alex 您能否详细说明这将提供哪些优势?虽然协程非常适合在处理程序中构建异步调用链(以及 Boost.Asio 1.54 API 的一部分),但我很难理解内部事件循环调度的好处。
  • @Tanner Sansbury 我想我们的意思是一样的。不要在 post() 方法中使用协程,而是在调用 post() 设置的任务链的处理程序时使用。并且会在 boost::asio 1.54 中有机会控制它们的使用吗?例如,对于短任务 - 使用协程来减少同步开销,对于长任务 - 使用线程进行负载平衡。

标签: boost boost-asio


【解决方案1】:

据我所知,Boost.Asio 不使用协程。

从实现的角度来看,我认为使用协程(例如 Boost.Coroutine 提供的协程)会在调用已发布的处理程序时引入开销。在事件循环知道可以调用什么 handlers 时,它可以简单地调用处理程序,而不必在蹦床函数中提升处理程序,以便可以在上下文中透明地调用它协程。

Boost.Asio 不知道 handlers 的实际或预期运行时间,因此它必须执行相同的内部同步,而不管处理程序如何。当io_service 仅由单个线程处理时,可以通过在构造期间提供concurrency_hint 来减轻同步开销。其他区域,例如反应堆,可能仍需要执行同步。

最后,Boost.Asio 并没有强加执行上下文,而是提供了一个强大的工具包,让用户能够为自己选择最佳选项。 Boost 1.54 的当前 Boost.Asio 候选版本通过其一流的支持增强了这种体验:

  • Stackful Coroutines 基于 Boost.Coroutine。这是一个示例,其中do_echomy_strand 的上下文中作为协程执行。每个异步操作在启动异步操作后将控制权交还给调用线程,当调用完成处理程序时,控制权会立即返回到上一个屈服点之后。

    boost::asio::spawn(my_strand, do_echo);
    
    // ...
    
    void do_echo(boost::asio::yield_context yield)
    {
      try
      {
        char data[128];
        for (;;)
        {
          std::size_t length =
            my_socket.async_read_some(
              boost::asio::buffer(data), yield);
    
          boost::asio::async_write(my_socket,
              boost::asio::buffer(data, length), yield);
        }
      }
      catch (std::exception& e)
      {
        // ...
      }
    }
    

    Boost.Asio 提供了一个完整的echo_service example,它使用了 Stackful Coroutines。

  • Stackless Coroutines 已从 HTTP Server 4 example 升级为记录在案的公共 API。这些是作为 Duff 设备的变体实现的,但通过使用伪关键字 reenteryieldfork,可以清楚地隐藏细节。下面这个大致相当于上面的 Stackful Coroutine 例子:

    struct session : boost::asio::coroutine
    {
      tcp::socket my_socket_;
      char data_[128];
    
      // ...
    
      void operator()(boost::system::error_code ec = boost::system::error_code(),
                      std::size_t length = 0)
      {
        if (!ec) reenter (this)
        {
          for (;;)
          {
            yield my_socket_.async_read_some(
                boost::asio::buffer(data_), *this);
    
            yield boost::asio::async_write(my_socket_,
                boost::asio::buffer(data_, length), *this);
          }
        }
      }
    };
    

    有关详细信息,请参阅boost::asio::coroutine 文档。


虽然我不知道使用协程构建异步调用链是否有性能优势,但我觉得它们最大的贡献是可维护性和可读性。我发现能够以同步方式读写异步程序有助于降低反向控制流引入的复杂性,因为现在可以消除操作启动和完成之间的空间分离。

【讨论】:

  • 谢谢!关于 concurrency_hint 的一个小问题:如果我用 10 个线程创建池,并在每个线程中启动 boost::asio::io_service::run,然后如果我将 concurrency_hint 设置为 20 或 5,那么将有多少线程使用 io_service?
  • @Alex:视情况而定。当io_service 实现使用Windows I/O 完成端口时,concurrency_hint 指定最大值,每个CreateIoCompletionPort()。对于非 Windows IOCP 实现,concurrency_hint 为 1 是当前应用优化的唯一情况。
猜你喜欢
  • 2012-11-26
  • 2019-01-22
  • 1970-01-01
  • 1970-01-01
  • 2011-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-18
相关资源
最近更新 更多