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

4 c++ boost asynchronous boost-asio c++11

我惊讶地发现下面的代码无需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;

}
Run Code Online (Sandbox Code Playgroud)

seh*_*ehe 6

Asio添加了关联执行器默认执行器的概念。

\n

关联的执行器并不是真正的新东西,因为handler_invoke协议已经允许处理程序类型特定的语义。然而,自从执行者概念提出以来,它变得更加普遍。

\n

现在您可以使用post任何处理程序,它将在关联的执行程序、提供的执行程序默认执行程序上执行。默认执行者是finally system_executor{}

\n

所以

\n
post([]{ puts("Hello world"); });\npost(system_executor{}, []{ puts("Hello world"); });\n
Run Code Online (Sandbox Code Playgroud)\n

两者都使用调用处理程序system_executor

\n

您可以将关联的处理程序与任何尚未关联的处理程序绑定:

\n
post(bind_executor(ex1, []{ puts("Hello world"); }));\npost(system_executor{}, bind_executor(ex1, []{ puts("Hello world"); }));\n
Run Code Online (Sandbox Code Playgroud)\n

两者都在 上运行处理程序ex1,而不是后备。结合以上内容,您已经预料到它会起到相同的作用:

\n
post(ex1, []{ puts("Hello world"); });\n
Run Code Online (Sandbox Code Playgroud)\n

(这里,处理程序没有关联执行程序,因此ex1充当后备)

\n

生成

\n

Spawn 只是“发布”另一种类型的处理程序\xc2\xb9 的包装器。事实上,它被记录为使用任何关联的执行器。实现非常易读地反映了这一点:

\n
template <typename Function>\ninline void spawn(BOOST_ASIO_MOVE_ARG(Function) function,\n    const boost::coroutines::attributes& attributes)\n{\n  typedef typename decay<Function>::type function_type;\n\n  typename associated_executor<function_type>::type ex(\n      (get_associated_executor)(function));\n\n  boost::asio::spawn(ex, BOOST_ASIO_MOVE_CAST(Function)(function), attributes);\n}\n
Run Code Online (Sandbox Code Playgroud)\n

您可以看到get_associated_executor调用时没有显式回退,默认为system_executor再次。

\n

旁注

\n

此外

\n
    \n
  • spawn将在适当的地方添加一条链(这就是为什么在构建执行上下文时提供并发提示可以产生很大差异的原因)
  • \n
  • spawn可以将 ayield_context作为第一个参数,在这种情况下,您将有效地在同一链上运行(共享执行器)
  • \n
\n
\n

\xc2\xb9 这是一个实现细节,但通常会boost::asio::detail::spawn_helper<...>再次正确传播关联的执行器/分配器。我将这种类型称为“处理程序活页夹”

\n

现场演示

\n

为了说明正在使用的实际情况system_executor,这里有一个简化的测试器:

\n

编译器资源管理器

\n
#include <boost/asio/spawn.hpp>\n#include <boost/asio/steady_timer.hpp>\n#include <iostream>\n\nint main() {\n    using namespace boost::asio;\n    using namespace std::chrono_literals;\n    io_context ctx(1);\n\n    spawn([](yield_context yield) {\n        std::cout << "started spawn" << std::endl;\n\n        auto ex = get_associated_executor(yield);\n        //auto work = make_work_guard(ex);\n\n        steady_timer timer(ex, 5s);\n        timer.async_wait(yield);\n\n        std::cout << "finished spawn" << std::endl;\n    });\n\n    std::cout << "running context" << std::endl;\n    query(system_executor{}, execution::context).join();\n    std::cout << "finished running context" << std::endl;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

笔记:

\n
    \n
  • ctx现在需要并发提示(如上所述)

    \n
  • \n
  • ctx从未使用过;加入它不会等待coro完成!

    \n
  • \n
  • 请注意评论work。重要的是,虽然异步操作构成工作,但Coro 本身并不工作,因此在某些情况下您可能需要保护 coro 的范围。

    \n
  • \n
  • 请注意,它system_executor的连接方式就像另一个基于线程的执行上下文一样 thread_pool

    \n
     query(system_executor{}, execution::context).join();\n
    Run Code Online (Sandbox Code Playgroud)\n
  • \n
\n

现在打印了

\n
started spawn\nrunning context\nfinished spawn\nfinished running context\n
Run Code Online (Sandbox Code Playgroud)\n

正如预期的那样,延迟了 5 秒。

\n