1

我正在使用协程玩 asio,并想测试如何调用异步函数。我有以下代码:

void async_foo(boost::asio::io_service& io_service, boost::asio::yield_context& yield)
{
    using handler_type = boost::asio::handler_type<decltype(yield), void()>::type;

    handler_type handler(std::forward<handler_type>(yield));
    boost::asio::async_result<decltype(handler)> result(handler);

    auto timer(std::make_shared<boost::asio::deadline_timer>(io_service, boost::posix_time::seconds(1)));
    // the program crashes if I use &handler here
    timer->async_wait([&handler](const boost::system::error_code) {
            std::cout << "enter" << std::endl;
            handler();
            std::cout << "done" << std::endl;
        });
    result.get();

    std::cout << "function finished" << std::endl;

    return;
}

int main()
{
    boost::asio::io_service io_service;

    boost::asio::spawn(io_service, [&io_service](boost::asio::yield_context yield) {
            std::cout << "hello" << std::endl;
            async_foo(io_service, yield);
            std::cout << "world" << std::endl;
        });
    io_service.run();
    return 0;
}

奇怪的是,如果我将 &handler 放在捕获列表中,执行流程将会混乱,然后会遇到分段错误。但是如果我使用“处理程序”代替它运行没有任何问题(那么我当然需要在 lambda 中的副本)。

搜索了一圈,找不到任何相关的东西。提前感谢您的帮助。

4

1 回答 1

1

正如 Tanner在这里很好地解释的那样:

  • spawn() 向(将启动并跳转到协程的处理程序)添加工作时io_service,协程本身不起作用。为了防止 io_service事件循环在协程未完成时结束,可能需要io_service在 yield 之前添加工作。

如果在 : 之后添加额外的跟踪行run()

io_service.run();
std::cout << "BYE" << std::endl;

然后,运行几次,直到你有幸没有提前获得 SEGV,你可以看到如下输出:

hello
enter
done
BYE

实际上,io_service 返回,破坏挂起的操作/处理程序,这也破坏了 coro¹ 的堆栈上下文。展开该堆栈会破坏该堆栈上的(本地)变量:

  • 处理程序
  • 结果
  • 计时器

并且由于timer未在 的完成处理程序中捕获async_wait,因此计时器被简单地取消,但仍尝试调用现在引用现已失效的堆栈变量的完成处理程序handler

显然复制handler²确实使 coro 保持活力。


要回答核心问题“并想测试如何调用异步函数”,我建议使用更简单的成语:

void async_foo(boost::asio::io_service& io_service, boost::asio::yield_context& yield)
{
    boost::asio::deadline_timer timer(io_service, boost::posix_time::seconds(1));
    boost::system::error_code ec;

    timer.async_wait(yield[ec]);

看见Live On Coliru


¹-fsanitize=address证实了这一点²我知道它包含weak_ptr协程上下文的 a ,所以也许 Asio 在内部将其锁定为 a shared_ptr,我自己不太确定这些实现细节

于 2017-04-14T10:59:40.223 回答