Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/155.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ boost::asio::yield\u上下文:意外的强制\u解除异常_C++_Boost_Boost Asio_Coroutine - Fatal编程技术网

C++ boost::asio::yield\u上下文:意外的强制\u解除异常

C++ 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

我正试图为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>

#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";
}