c++ - 什么时候必须将 io_context 传递给 boost::asio::spawn? (C++)

标签 c++ c++11 asynchronous boost boost-asio

我惊讶地发现下面的代码在没有通过 io_context 的情况下工作。作为 spawn 的第一个参数.有人可以解释一下为什么在这种情况下我不需要通过它,以及在什么情况下你必须明确地通过它。 我正在使用 Boost 1.75.0 .

#include <boost/asio/spawn.hpp>
#include <boost/asio/deadline_timer.hpp>
#include <iostream>

int main() {

  boost::asio::io_context io_context;
  boost::asio::deadline_timer timer(io_context);

  boost::asio::spawn([&](boost::asio::yield_context yield){ // don't need to pass io_context?!
    std::cout << "started spawn" << std::endl;
    timer.expires_from_now(boost::posix_time::seconds(5));
    timer.async_wait(yield);
    std::cout << "finished spawn" << std::endl;
  });

  std::cout << "running io_context" << std::endl;
  io_context.run();
  std::cout << "finished running io_context" << std::endl;

}

最佳答案

Asio 增加了关联执行器和默认执行器的概念。
相关的执行器并不是真正的新,因为 handler_invoke协议(protocol)已经允许处理程序类型特定的语义。然而,由于执行者概念的形成,它变得更加普遍。
现在你可以post任何处理程序,它将在关联的执行程序上执行,执行程序提供 默认执行者。默认执行器最终是 system_executor{} .
所以

post([]{ puts("Hello world"); });
post(system_executor{}, []{ puts("Hello world"); });
两个处理程序都使用 system_executor .
您可以将关联的处理程序与尚未关联的任何处理程序绑定(bind):
post(bind_executor(ex1, []{ puts("Hello world"); }));
post(system_executor{}, bind_executor(ex1, []{ puts("Hello world"); }));
两者都在 ex1 上运行处理程序,而不是后备。结合以上内容,您已经期望这也是如此:
post(ex1, []{ puts("Hello world"); });
(这里,处理程序没有关联执行程序,所以 ex1 用作后备)
产卵
Spawn 只是一个“发布”另一种处理程序¹的包装器。事实上,它记录了使用任何相关的执行程序。该实现非常清晰地反射(reflect)了这一点:
template <typename Function>
inline void spawn(BOOST_ASIO_MOVE_ARG(Function) function,
    const boost::coroutines::attributes& attributes)
{
  typedef typename decay<Function>::type function_type;

  typename associated_executor<function_type>::type ex(
      (get_associated_executor)(function));

  boost::asio::spawn(ex, BOOST_ASIO_MOVE_CAST(Function)(function), attributes);
}
你可以看到get_associated_executor在没有显式回退的情况下调用,默认为 system_executor再次。
旁注
此外
  • spawn将在适当的地方添加一个链(这就是为什么在构建执行上下文时提供并发提示会产生很大影响的原因)
  • spawn可以拍yield_context作为第一个参数,在这种情况下,您将有效地在同一条链上运行(共享执行程序)

  • ¹ 这是一个实现细节,但通常是 boost::asio::detail::spawn_helper<...>它再次正确传播关联的执行器/分配器。我将这种类型称为“处理程序活页夹”
    现场演示
    为了说明 system_executor 的现实正在使用,这是一个简化的测试器:
    Compiler Explorer
    #include <boost/asio/spawn.hpp>
    #include <boost/asio/steady_timer.hpp>
    #include <iostream>
    
    int main() {
        using namespace boost::asio;
        using namespace std::chrono_literals;
        io_context ctx(1);
    
        spawn([](yield_context yield) {
            std::cout << "started spawn" << std::endl;
    
            auto ex = get_associated_executor(yield);
            //auto work = make_work_guard(ex);
    
            steady_timer timer(ex, 5s);
            timer.async_wait(yield);
    
            std::cout << "finished spawn" << std::endl;
        });
    
        std::cout << "running context" << std::endl;
        query(system_executor{}, execution::context).join();
        std::cout << "finished running context" << std::endl;
    }
    
    笔记:
  • ctx现在需要一个并发提示(如上所述)
  • ctx没用过 ;加入它不会等待 coro 完成!
  • 注意评论 work .重要的是,尽管异步操作构成工作,Coro itself is not work所以在某些情况下你可能想保护 coro 的范围。
  • 请注意 system_executor像你一样加入另一个基于线程的执行上下文,如 thread_pool :
     query(system_executor{}, execution::context).join();
    

  • 现在它打印
    started spawn
    running context
    finished spawn
    finished running context
    
    与预期的5s延迟。

    关于c++ - 什么时候必须将 io_context 传递给 boost::asio::spawn? (C++),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66731990/

    相关文章:

    c++ - 适用于嵌入式 Windows 的 Silverlight

    c++错误错误C2512没有合适的默认构造函数可用

    c++ - 为什么 C++11 删除的函数会参与重载决议?

    c++ - 无法从 stringw 转换为 string/char

    C++ 将模板类型限制为数字

    javascript - Node.Js 中的异步问题 - 如何执行一系列操作?

    c++ - 有限状态机编译器

    c++ - 重载 'swap(float&, float&)' 的调用不明确

    javascript - 如何正确使用Promise.all?

    c# - 在任何情况下,在 ReaderWriterLockSlim 上调用 EnterWriteLock 应该进入读锁吗?