C++ boost::asio::yield\u上下文:意外的强制\u解除异常
我正试图为boost::asio编写自定义异步函数,如前所述 然而,我得到了boost::coroutines::detail::forced_unwind异常,结果为result.getC++ boost::asio::yield\u上下文:意外的强制\u解除异常,c++,boost,boost-asio,coroutine,C++,Boost,Boost Asio,Coroutine,我正试图为boost::asio编写自定义异步函数,如前所述 然而,我得到了boost::coroutines::detail::forced_unwind异常,结果为result.get #include <boost/chrono.hpp> #include <boost/asio.hpp> #include <boost/asio/spawn.hpp> #include <boost/asio/steady_timer.hpp> #incl
#include <boost/chrono.hpp>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/steady_timer.hpp>
#include <iostream>
namespace asio = ::boost::asio;
template <typename Timer, typename Token>
auto my_timer (Timer& timer, Token&& token)
{
typename asio::handler_type<Token,
void (::boost::system::error_code const)>::type
handler (std::forward<Token> (token));
asio::async_result<decltype (handler)> result (handler);
timer.async_wait (handler);
return result.get (); // Got forced_unwind exception here.
}
int main ()
{
asio::io_service io;
asio::steady_timer timer (io, ::boost::chrono::seconds (1));
asio::spawn (io, [&] (asio::yield_context yield)
{
try {
std::cout << "my_timer enter\n";
my_timer (timer, yield);
std::cout << "my_timer returns\n";
}
catch (const boost::coroutines::detail::forced_unwind& e)
{
std::cout << "boost::coroutines::detail::forced_unwind\n";
}
}
);
io.run ();
}
及
这是一个boostcoroutine实现细节 如本文所述:
try {
std::cout << "my_timer enter\n";
my_timer(timer, yield);
std::cout << "my_timer returns\n";
}
catch (boost::coroutines::detail::forced_unwind const& e)
{
throw; // required for Boost Coroutine!
}
catch (std::exception const& e)
{
std::cout << "exception '" << e.what() << "'\n";
}
⚠ 重要的
协同程序函数执行的代码不得阻止detail::forced_unwind异常的传播。吸收该异常将导致堆栈展开失败。因此,捕获所有异常的任何代码都必须重新抛出任何挂起的异常
因此,您需要显式地传递此异常。显式编写处理程序,如下所示:
try {
std::cout << "my_timer enter\n";
my_timer(timer, yield);
std::cout << "my_timer returns\n";
}
catch (boost::coroutines::detail::forced_unwind const& e)
{
throw; // required for Boost Coroutine!
}
catch (std::exception const& e)
{
std::cout << "exception '" << e.what() << "'\n";
}
试试看{
std::cout简而言之,您需要创建处理程序的副本,例如在尝试获取async\u结果之前将其发布到io\u服务
,以保持协同程序处于活动状态
Boost.Asio通过销毁协程来防止不可恢复的协程无限期挂起,从而导致协程的堆栈展开。协程对象将在其销毁过程中抛出Boost::coroutines::detail::forced_unwind
,导致挂起的堆栈展开。Asio通过以下方式完成此操作:
- CompletionToken维护对协同路由的
弱\u ptr
- 构造专用处理程序时,它通过CompletionToken的
弱\u ptr
为协程获取共享\u ptr
。当处理程序作为完成处理程序传递给异步操作时,将复制处理程序及其共享\u ptr
。调用处理程序时,它将恢复协程李>
- 调用时,专门化将重置在构造期间传递给
async\u result
的处理程序所拥有的协程shared\u ptr
,然后生成协程
这里试图说明代码的执行。|
中的路径表示活动堆栈,:
表示挂起的堆栈,箭头用于表示控制权的转移:
boost::asio::io_服务io_服务;
boost::asio::spawn(io_服务和my_计时器);
`--分派一个协同程序创建者
进入io_服务。
io_service.run();
|--调用协同程序条目
|处理程序。
||--创建协同程序
||(计数:1)
||--启动协同程序-->我的计时器()
:|--创建句柄1(计数:2)
:|--创建asnyc|U结果1(handler1)
:|--timer.async_wait(处理程序)
:| |--创建句柄2(计数:3)
::| |--创建异步结果2(handler2)
::| |--创建操作和复制
:| | handler3(计数:4)
::|`--async_result2.get()
::| |--handler2.reset()(计数:3)
|`--return|--async_result1.get()
::| |--handler1.reset()(计数:1)
|“--return |”`--push forced|u unwind
要解决此问题,需要复制处理程序
,并在恢复协同程序时通过调用。例如,以下内容将向调用处理程序
副本的io_服务
发布完成句柄1:
timer.async\u等待(处理程序);
timer.get_io_service().post(
std::bind([](decltype(handler)handler)
{
boost::system::error\u code error;
//处理程序必须通过asio_处理程序_调用挂钩调用
//要与协同程序的执行正确同步
//上下文。
使用boost::asio::asio\u处理程序\u调用;
asio_处理程序_调用(std::bind(处理程序,错误),&handler);
},处理程序)
);
返回result.get();
如图所示,使用此附加代码,输出变为:
myu定时器输入
我的计时器返回
1.完成处理程序代码可能会被清理一点,但在我回答时,我观察到一些编译器选择了错误的asio\u handler\u invoke
hook。@sehe you you right,我的示例代码不正确,我错过了强制释放重试。但是,请注意,即使您添加了重试,代码也不会按预期工作。我希望“my_timer enter”和“my_timer return”输出,但实际上我只收到了“my_timer enter”消息。我打赌boost 1.54和VM也会看到同样的消息。@NikkiChumakov是的。我认为还有其他问题。处理程序的结果类型会被推断为void吗?(我将auto
替换为void
以在c++11上编译)。这似乎是Boost协同程序取消NDI的原因之一。我不知道原因是什么,只是想找出原因。:)我注意到的是,如果我用一些简单的包装类甚至lambda包装async_wait处理程序,那么一切都会按预期工作。这是因为随后为async_结果
trait和handler_invoke
定制点选择了不同的专门化/ADL重载。(老实说,我没有跟上你在这里想要实现的目标。我有点假设你知道:))我直到今天才看到这个问题,但要点是Boost。Asio可以防止一个协程无限期地暂停,如果它知道没有可用的处理程序来恢复它。当这种情况发生时,Boost。Asio将破坏该协程,导致暂停的堆栈展开。谢谢你的回答。喝了几杯咖啡后,我理解了ood为什么发布的std::bind处理程序总是在协程销毁后调用,即使在多线程io_serv中也是如此
try {
std::cout << "my_timer enter\n";
my_timer(timer, yield);
std::cout << "my_timer returns\n";
}
catch (boost::coroutines::detail::forced_unwind const& e)
{
throw; // required for Boost Coroutine!
}
catch (std::exception const& e)
{
std::cout << "exception '" << e.what() << "'\n";
}