执行取消时,boost :: asio :: yield_context是否可以用作deadline_timer处理程序? [英] Can a boost::asio::yield_context be used as a deadline_timer handler when doing cancel?

查看:531
本文介绍了执行取消时,boost :: asio :: yield_context是否可以用作deadline_timer处理程序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想能够对特定事件进行异步等待。有很多类似的问题和答案在这里(所有编译和工作为我),但没有与我的具体情况。基本上,我需要做的是一个async_wait,传递一个yield上下文作为处理程序,到一个定时器无限期等待,然后被另一个线程取消。

I'd like to be able to do an asynchronous wait on a specific event. There are a lot of similar questions and answers on here (and all compile and work for me) but none with my specific scenario. Basically, what I need to be able to do is an async_wait, passing a yield context as the handler, to a timer that waits indefinitely, and is then canceled by another thread.

例如,有这个问题,它做的事情非常相似,但是它使用一个单独的独立处理程序,而不是使用yield上下文。还有一些像

For example, there is this question which does something very similar, but instead of using a yield context, it uses a separate, standalone handler. There is also something like this question which uses a yield context, but waits for a specified amount of time.

我使用一个yield语境来处理这个问题。可以改变我的代码看起来像上面的两个例子,事情工作正常。但是对于某些原因,当我结合yield_context处理程序和取消的计时器,我得到以下异常:

I can change my code to look like either of the two examples above and things work fine. But for someone reason when I combine a yield_context handler and a cancelled timer, I get the following exception:

libc++abi.dylib: terminating with uncaught exception of type boost::exception_detail::clone_impl<boost::exception_detail::current_exception_std_exception_wrapper<std::runtime_error> >: 
Program ended with exit code: 9

当尝试调用完成处理程序(在这种情况下是yield上下文)时,看起来像东西窒息。

From what I can tell, it looks like things choke when trying to invoke the completion handler (which in this case is the yield context).

好了,足够的说明,这里是代码。我试着想出尽可能简单的例子来说明它:

Alright, enough babbling, here's the code. I've tried to come up with as simple of an example as possible to illustrate it:

类:

class Foo {
public:
  Foo() : work_(io_service_), timer_(io_service_) {
    thread_pool_.create_thread(boost::bind(&boost::asio::io_service::run, &io_service_));
    timer_.expires_from_now(boost::posix_time::pos_infin);
  }
  ~Foo() {
    io_service_.stop();
    thread_pool_.join_all();
  }

  void Wait(const boost::asio::yield_context& context) {
    std::cout << "Waiting" << std::endl;
    timer_.async_wait(context);
    std::cout << "Done waiting" << std::endl;
  }

  void Notify() {
    std::cout << "Notifying" << std::endl;
    timer_.cancel();
  }

  void Write(int num) {
    std::cout << "Sending buffer event" << std::endl;
    Notify();
    std::cout << "Sent buffer event" << std::endl;
  }

  void Read(const boost::asio::yield_context& context) {
    std::cout << "Waiting on buffer event, size is " << buffer_.size() << std::endl;
    Wait(context);
    std::cout << "Received buffer event, size is now " << buffer_.size() << std::endl;
  }

  std::vector<int> buffer_;
  boost::asio::io_service io_service_;
  boost::thread_group thread_pool_;
  boost::asio::io_service::work work_;
  boost::asio::deadline_timer timer_;
};

主要:

boost::shared_ptr<Foo> foo(new Foo());    
boost::asio::spawn(foo->io_service_, boost::bind(&Foo::Read, foo, _1));
boost::this_thread::sleep(boost::posix_time::seconds(2));
foo->Write(1);
boost::this_thread::sleep(boost::posix_time::seconds(4));

输出:

Waiting on buffer event
Waiting
Sending buffer event
Notifying
Sent buffer event
libc++abi.dylib: terminating with uncaught exception of type boost::exception_detail::clone_impl<boost::exception_detail::current_exception_std_exception_wrapper<std::runtime_error> >: 

现在,如果我将等待方法更改为在取消之前超时的时间被称为,一切都很好。 Ie:

Now, if I change the wait method to a time that will time out before the cancel is called, everything is fine. I.e.:

void Wait(const boost::asio::yield_context& context) {
    std::cout << "Waiting" << std::endl;
    timer_.expires_from_now(boost::posix_time::seconds(1));
    timer_.async_wait(context);
    std::cout << "Done waiting" << std::endl;
  }

或者,如果我改变等待使用一个单独的处理程序方法,一切都很好。即:

Or, if I change wait to use a separate handler method, everything is fine. I.e.:

void Handler() {
  std::cout << "Handler!" << std::endl;
}

void Wait(const boost::asio::yield_context& context) {       
  std::cout << "Waiting" << std::endl;
  timer_.async_wait(boost::bind(&Foo::Handler, this));
  std::cout << "Done waiting" << std::endl;
}

我假设有一些更简单我在这里缺少:这是不可能的,因为某种原因,或者我犯了一些愚蠢的错误。

I'm assuming there must be something simpler I'm missing here: either this is impossible for some reason or I'm making some dumb mistake. Anyway, thanks in advance.

推荐答案

async_wait()操作是被取消,导致异步操作失败,错误代码 boost :: asio :: error :: operation_aborted 。如 Stackful Coroutines文档中所述, a>,当 boost :: asio :: yield_context 检测到异步操作失败时,它会转换 boost :: system :: error_code 转换为 system_error 异常并抛出。在协程中,考虑:

The async_wait() operation is being cancelled, resulting in the asynchronous operation failing with an error code of boost::asio::error::operation_aborted. As noted in the Stackful Coroutines documentation, when the boost::asio::yield_context detects that the asynchronous operation has failed, it converts the boost::system::error_code into a system_error exception and throws. Within the coroutine, consider either:

  • Initiating the asynchronous operation with a handler of context[error_code], causing the yield_context to populate the provided boost::system::error_code on failure rather than throwing.

boost::system::error_code error;
timer_.async_wait(context[error]); // On failure, populate error.


  • 捕获 system_error

    发生故障Boost.Asio将填充 boost :: system :: error_code 如果应用程序能够接收它,否则会抛出异常。此模式可以在Boost.Asio中观察到:

    On failure Boost.Asio will populate a boost::system::error_code if the application is capable of receiving it, otherwise it will throw an exception. This pattern can be observed throughout Boost.Asio:


    • 全部异步操作处理程序接受一个值code> const boost :: system :: error_code 作为它们的第一个参数。因此,启动函数不应该抛出,因为应用程序将被通知处理程序中的错误。当使用舍弃额外参数的函子时,这并不总是很明显,例如 boost :: bind

    • 同步操作重载以支持投掷和非投掷版本。例如, timer.cancel() 会抛出失败,其中 timer.cancel(boost :: system :: error_code&) http://www.boost.org/doc/libs/1_55_0/doc/html/boost_asio/reference/basic_yield_context.html =nofollow> yield_context 处理程序未提供 boost :: system :: error_code ,则将抛出 system_error 异常。

    • 使用 futures ,如果异步操作失败,则 error_code 将转换为 system_error 异常并通过未来返回给调用者。

    • All asynchronous operation handler's accept an lvalue const boost::system::error_code as their first parameter. Hence, the initiating function should not throw, as the application will be informed of the error within the handler. This is not always apparent when using functors that discards extra arguments, such as boost::bind.
    • Synchronous operations are overloaded to support throwing and non-throwing versions. For example, timer.cancel() will throw on failure, where as timer.cancel(boost::system::error_code&) will set the error_code to indicate the error.
    • As noted above, when an asynchronous operation fails within a stackful coroutine and the yield_context handler is not provided a boost::system::error_code, then a system_error exception will be thrown.
    • When using futures, if the asynchronous operation fails, then the error_code is converted into a system_error exception and passed back to the caller through the future.

    这是一个完整的最小示例,基于运行到完成的原始问题。

    Here is a complete minimal example based on the original problem that runs to completion.

    #include <boost/asio.hpp>
    #include <boost/asio/spawn.hpp>
    
    int main()
    {
      boost::asio::io_service io_service;
      boost::asio::deadline_timer timer(io_service);
      timer.expires_from_now(boost::posix_time::pos_infin);
    
      boost::asio::spawn(io_service,
        [&](boost::asio::yield_context yield)
        {
          // As only one thread is processing the io_service, the posted
          // timer cancel will only be invoked once the coroutine yields.
          io_service.post([&](){ timer.cancel(); });
    
          // Initiate an asynchronous operation, suspending the current coroutine,
          // and allowing the io_service to process other work (i.e. cancel the 
          // timer).  When the timer is cancelled, the asynchronous operation is
          // completed with an error,  causing the coroutine to resume.  As an
          // error_code is provided, the operation will not throw on failure.
          boost::system::error_code error;
          timer.async_wait(yield[error]);
          assert(error == boost::asio::error::operation_aborted);
        });
    
      io_service.run();
    }
    

    这篇关于执行取消时,boost :: asio :: yield_context是否可以用作deadline_timer处理程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    相关文章
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆