执行取消时,boost :: asio :: yield_context是否可以用作deadline_timer处理程序? [英] Can a boost::asio::yield_context be used as a deadline_timer handler when doing cancel?
问题描述
我想能够对特定事件进行异步等待。有很多类似的问题和答案在这里(所有编译和工作为我),但没有与我的具体情况。基本上,我需要做的是一个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 theyield_context
to populate the providedboost::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&)