在协程之前,当从回调中抛出异常时,例如通过
boost::asio::post
,异常将从 boost::asio::io_context::run()
传播出去。然而,如果通过 boost::asio::co_spawn
使用协程,在像 boost::asio::detached
这样的即发即忘模式下,异常不会从 run()
中抛出。在类似的问题中,一个答案,https://stackoverflow.com/a/68042350/3537677,提到使用带有void (std::exception_ptr,...)
签名的完成处理程序,但是我根本无法在该处理程序中执行代码。
那么如何从 co_routines 中获取异常并传播到
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"; //Doesn't get executed
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...
std::cout << "Rethrowing in completion handler\n";
Exception in context::run(): Test throw from coroutine!
Exception in context::run(): Test throw from post!
Process finished with exit code 0
throw ptr
并没有像你想象的那样做。
使用
if (ptr) {
std::cout << "Rethrowing in completion handler" << std::endl;
std::rethrow_exception(ptr);
}
接下来,处理
io_context
的异常略有不同:您提前停止 io_context
。
稍微简化和改进输出:
#include <iostream>
#include <boost/asio.hpp>
boost::asio::awaitable<void> coroutine() {
std::cout << "Coroutine executes..." << std::endl;
throw std::runtime_error("Test throw from coroutine!");
co_return;
}
int main() {
boost::asio::io_context context;
boost::asio::co_spawn(context, coroutine, boost::asio::detached);
boost::asio::co_spawn(context, coroutine, [](std::exception_ptr ptr) {
if (ptr) {
std::cout << "Rethrowing in completion handler" << std::endl;
std::rethrow_exception(ptr);
} else {
std::cout << "Completed without error" << std::endl;
}
});
boost::asio::post(context, [] { throw std::runtime_error("Test throw from post!"); });
while (true) {
try {
context.run();
break;
} catch (std::exception const& e) {
std::cerr << "Exception in context::run(): " << e.what() << std::endl;
}
}
}
打印
Coroutine executes...
Coroutine executes...
Exception in context::run(): Test throw from post!
Rethrowing in completion handler
Exception in context::run(): Test throw from coroutine!