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,...)的完成处理器,但该处理器未执行。
需求:
- 如何让协程中的异常传播至
io_context::run()? - 若无法实现,如何为
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
相关产品推荐
相关产品推荐

