我惊讶地发现下面的代码在没有通过 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/