考虑以下代码:
#include <boost/asio/awaitable.hpp>
#include <boost/asio/bind_cancellation_slot.hpp>
#include <boost/asio/cancellation_signal.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/use_awaitable.hpp>
namespace ba = boost::asio;
ba::cancellation_signal cancel_sub;
void subscribe(ba::io_context &context)
{
ba::co_spawn(
context,
[]() -> ba::awaitable<void> { co_return; },
ba::bind_cancellation_slot(cancel_sub.slot(), ba::detached));
cancel_sub.emit(ba::cancellation_type::all);
}
int main()
{
ba::io_context ctx;
subscribe(ctx);
ctx.run();
return 0;
}
它使我的程序退出,并从 boost 中的 co_await 实现的内部抛出异常:
terminate called after throwing an instance of 'boost::wrapexcept<boost::system::system_error>'
what(): co_await: Operation canceled [system:125]
如何避免这种情况?
我尝试过:
co_await ba::this_coro::throw_if_cancelled(false);
co_await ba::this_coro::reset_cancellation_state(ba::enable_total_cancellation());
没有任何变化,我的应用程序仍然退出。
准确地说,在boost的代码中:(用
// !!!
标记我的评论)
template <typename Handler, typename Executor, typename Function>
awaitable<awaitable_thread_entry_point, Executor> co_spawn_entry_point(
awaitable<void, Executor>*, co_spawn_state<Handler, Executor, Function> s)
{
(void) co_await co_spawn_dispatch{};
(co_await awaitable_thread_has_context_switched{}) = false;
std::exception_ptr e = nullptr;
try
{
// !!! here an exception is thrown as the cancellation is observered.
co_await s.function();
}
catch (...)
{
// !!! caught here, all is fine
e = std::current_exception();
}
bool switched = (co_await awaitable_thread_has_context_switched{});
if (!switched)
(void) co_await co_spawn_post(); // !!! exception thrown again here as the cancellation state is checked again in await_transform! But now there is nothing to catch it...
(dispatch)(s.handler_work.get_executor(),
[handler = std::move(s.handler), e]() mutable
{
std::move(handler)(e);
});
}
没有任何变化,我的应用程序仍然退出。
如果您遇到异常,但不希望应用程序退出,如何将原始协程逻辑包装在 try-catch 块中来处理所述异常,并防止其传播到协程之外?
它可以工作,因为您面临的问题与取消操作引发的异常有关,该异常会导致异常,如果不处理该异常,则会导致您的程序终止,而不是由
return_value
或 return_void
(与协程相关的异常) return 机制),如 Raymond Chen 的文章“C++ 协程:如果我的 return_value 中发生异常会发生什么?”中讨论。
void subscribe(ba::io_context& context) {
ba::co_spawn(context, []() -> ba::awaitable<void> {
try {
// Your original coroutine logic
co_return;
} catch (const boost::system::system_error& e) {
// Handle cancellation or other system errors
// For example, log the error and continue without rethrowing
std::cerr << "Coroutine canceled or failed: " << e.what() << std::endl;
// You can also handle or log the cancellation specifically
if (e.code() == boost::asio::error::operation_aborted) {
std::cerr << "Operation was explicitly canceled.\n";
}
}
}, ba::bind_cancellation_slot(cancel_sub.slot(), ba::detached));
cancel_sub.emit(ba::cancellation_type::all);
}