You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

boost::asio::co_spawn异常无法传播至io_context::run的解决方法

问题

在协程出现前,通过boost::asio::post提交的回调抛出异常时,异常会从boost::asio::io_context::run()中传播出来。但用boost::asio::co_spawn创建协程并以boost::asio::detached模式运行时,异常不会从run()中抛出。尝试使用签名为void (std::exception_ptr,...)的完成处理器,但该处理器未执行。

需求:

  1. 如何让协程中的异常传播至io_context::run()?
  2. 若无法实现,如何为co_spawn定义异常处理逻辑?

最小复现示例(MRE)

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

boost::asio::awaitable<void>  coroutine() {
    std::cout << "Coroutine executes...\n";
    throw std::runtime_error("Test throw from coroutine!");
    co_return;
}

int main(int argc, char* argv[]) {
    boost::asio::io_context context;

    boost::asio::co_spawn(context, coroutine(), boost::asio::detached);
    boost::asio::co_spawn(context, coroutine(), [] (std::exception_ptr ptr) {
        std::cout << "Rethrowing in completion handler\n"; //未执行
        throw ptr;
    });

    boost::asio::post(context, [] () {
        throw std::runtime_error("Test throw from post!");
    });

    std::thread t([&context]() {
        try {
            while (true) {
                context.run();
                return;
            }
        } catch (std::exception &e) {
            std::cerr << "Exception in context::run(): " << e.what() << "\n";
        }
    });
    t.join();
}

实际输出

Coroutine executes...
Coroutine executes...
Exception in context::run(): Test throw from post!

Process finished with exit code 0

期望输出

...
Coroutine executes...
Rethrowing in completion handler
Exception in context::run(): Test throw from coroutine!
Exception in context::run(): Test throw from post!
Process finished with exit code 0

解决方案

1. 自定义完成处理器未执行的原因

你使用的void(std::exception_ptr)签名不符合co_spawn的要求:当协程返回awaitable<void>时,完成处理器的正确签名是void()(无参数)。此时若协程抛出异常,Asio会直接在执行完成处理器时重新抛出该异常,而非传递std::exception_ptr。只有当协程返回非void类型的awaitable时,完成处理器才需要接收std::exception_ptr和返回值参数。

2. 让协程异常传播至io_context::run()

修改完成处理器为无参数的void()签名,Asio会自动在处理器执行时重新抛出协程中的异常,该异常会从io_context::run()中传播出来,被你包裹run()的try-catch捕获:

boost::asio::co_spawn(context, coroutine(), []() {
    // 协程正常结束时执行此处;若协程抛出异常,Asio会直接在此处抛出
});

3. 自定义异常处理逻辑

如果不想让异常传播到run(),可以通过两种方式处理:

  • 协程内部捕获异常:
    在协程代码中直接捕获异常,自行处理或选择性重新抛出:

    boost::asio::awaitable<void> coroutine() {
        try {
            std::cout << "Coroutine executes...\n";
            throw std::runtime_error("Test throw from coroutine!");
            co_return;
        } catch (const std::exception& e) {
            std::cerr << "Coroutine internal catch: " << e.what() << "\n";
            // 若需要让异常继续传播到完成处理器,取消下面的注释
            // throw;
        }
    }
    
  • 使用use_future异步获取异常:
    通过boost::asio::use_future将协程的结果或异常绑定到std::future,在其他线程等待并处理:

    auto fut = boost::asio::co_spawn(context, coroutine(), boost::asio::use_future);
    std::thread t([&fut]() {
        try {
            fut.get();
        } catch (const std::exception& e) {
            std::cerr << "Future caught exception: " << e.what() << "\n";
        }
    });
    t.join();
    

4. 修改后的完整示例

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

boost::asio::awaitable<void> coroutine() {
    std::cout << "Coroutine executes...\n";
    throw std::runtime_error("Test throw from coroutine!");
    co_return;
}

int main(int argc, char* argv[]) {
    boost::asio::io_context context;

    // detached模式:异常不会自动传播,需自行在协程内处理
    boost::asio::co_spawn(context, coroutine(), boost::asio::detached);

    // 正确签名的完成处理器,异常会从run()中传播
    boost::asio::co_spawn(context, coroutine(), []() {});

    boost::asio::post(context, [] () {
        throw std::runtime_error("Test throw from post!");
    });

    std::thread t([&context]() {
        try {
            context.run();
        } catch (std::exception &e) {
            std::cerr << "Exception in context::run(): " << e.what() << "\n";
            // 处理第一个异常后,重置io_context继续处理剩余任务
            context.restart();
            context.run();
        }
    });
    t.join();
}

修改后的输出

Coroutine executes...
Coroutine executes...
Exception in context::run(): Test throw from coroutine!
Coroutine executes...
Exception in context::run(): Test throw from post!

Process finished with exit code 0

内容的提问来源于stack exchange,提问作者Superlokkus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 15:07:37