boost::asio::co_spawn 不会传播异常

cma*_*t85 5 c++ boost exception coroutine

我正在涉足关于 boost::asio 的协程,并且我对异常处理感到困惑。从文档中的示例 来看,看起来任何“失败”都会变成异常 - 所以我希望假设抛出的任何异常也会传播回调用。但情况似乎并非如此:error_codeco_spawn

#define BOOST_ASIO_HAS_CO_AWAIT
#define BOOST_ASIO_HAS_STD_COROUTINE

#include <iostream>

#include <boost/asio/awaitable.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/executor_work_guard.hpp>

namespace this_coro = boost::asio::this_coro;

boost::asio::awaitable<void> async_op()
{
    std::cout << "About to throw" << std::endl;
    throw std::runtime_error{"Bang!"};
}

int main()
{
    auto ctx = boost::asio::io_context{};
    auto guard = boost::asio::make_work_guard(ctx.get_executor());

    boost::asio::co_spawn(ctx, async_op, boost::asio::detached);

    ctx.run();
}
Run Code Online (Sandbox Code Playgroud)

如果这是在调试器中运行,您可以看到抛出异常,但它似乎只是挂起。暂停调试器表明正在ctx.run()等待新的工作(由于executor_work_guard)。所以看起来 boost::asio 内部的某些东西已经默默地吞噬了异常。

作为实验,我将异步操作切换为使用 boost::asio 库调用:

boost::asio::awaitable<void> async_op()
{
    auto executor = co_await this_coro::executor;
    auto socket = boost::asio::ip::tcp::socket{executor};

    std::cout << "Starting resolve" << std::endl;
    auto resolver = boost::asio::ip::tcp::resolver{executor};
    const auto endpoints = co_await resolver.async_resolve("localhost",
                                                           "4444",
                                                           boost::asio::use_awaitable);



    std::cout << "Starting connect (num endpoints: " << endpoints.size() << ")" << std::endl;
    co_await boost::asio::async_connect(socket, endpoints, boost::asio::use_awaitable);
    std::cout << "Exited" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)

我没有在端口 4444 上运行的服务器,因此这应该立即失败 - 而且它会默默地失败。暂停调试器表明它卡在 epoll 中等待某些东西(我在 Linux 上)。

将 交换async_connect CompletionToken为 aboost::asio::redirect_error显示操作失败:

co_await boost::asio::async_connect(socket,
                                    endpoints, 
                                    boost::asio::redirect_error(boost::asio::use_awaitable, ec));
std::cout << "Exited: " << ec.message() << std::endl;
Run Code Online (Sandbox Code Playgroud)

产量:

Starting resolve
Starting connect (num endpoints: 1)
Exited: Connection refused
Run Code Online (Sandbox Code Playgroud)

那么,如何传播异常,并从error_codeboost::asio 中的协程中的 s 创建异常?

Cos*_*rvu 3

boost::asio::co_spawn创建一个单独的线程。这意味着不会传播异常。您可以在这里阅读更多相关内容:

co_spawn支持带有签名的完成处理程序void(std::exception_ptr, R)。在您的示例中,您使用的boost::asio::detached这意味着忽略完成结果。要传播它,只需编写一个自定义处理程序

  • 虽然解决方案是正确的,但推理不是:`asio::co_spawn`实际上并不“创建一个单独的线程”,而是一个在指定的执行器上运行的执行线程。在OP的示例中,执行器在同一线程上运行,因此协程也是如此。`asio::co_spawn` 的实现只是捕获所有抛出的错误并将它们作为 `std::exception_ptr` 传递到完成处理程序中。`asio::detached` 恰好没有执行任何异常操作。 (2认同)