我尝试等待从任何std::future
函数返回的boost::asio::async_
对象(使用use_future
)。例如:
auto endpoint_future = resolver.async_resolve(query, boost::asio::use_future);
endpoint_future.wait(); // lasts forever after io_service.stop();
这是在一个线程中完成的。
我还有另一个在async_resolve
调用之前启动的线程:
runner_ = std::thread([this]() {
boost::asio::io_service::work work(io_service_);
io_service_.run();
});
一切都很好但后来我还添加了boost::asio::deadline_timer
来阻止io_service
的任何工作:
void deadlineTimeout() {
deadline_.cancel();
io_service_.stop();
// closing socket here does not work too
}
但是,在deadlineTimeout()
截止日期达到超时且执行io_service_.stop()
时,未来不会被释放,因此endpointer_future.wait()
仍会阻止。在这种情况下,我怎么能停止等待未来?
答案 0 :(得分:2)
我自己找到了一个解决方案:我们不需要stop()
io_service
而是reset()
它,在此之前我们需要关闭套接字,因此正确的超时回调将是:
void deadlineTimeout() {
deadline_.cancel();
socket_.close(); // socket_ is a socket on io_service_
io_service_.reset();
}
在此更改后,所有期货都将被释放。
答案 1 :(得分:2)
对io_sevice.stop()
的调用将导致所有run()
和run_one()
的所有调用都尽快返回。从处理程序中调用时,调用者将从run()
返回,而不调用任何其他处理程序。在您的情况下,async_resolve
的完成处理程序将设置与promise
相关联的endpoint_future
;但是,通过停止io_service
,将不会调用完成处理程序。考虑一下:
cancel()
与future
关联的I / O对象,然后继续运行io_service
以完成promise
的值设置io_service
以便删除处理程序并且future
循环执行未来的定时等待,并在未来准备就绪或io_service
已停止时退出循环。例如,以下函数返回带有未来值的boost::optional
,如果未设置将来,则返回boost::none
。
template <typename T>
boost::optional<T> get(
boost::asio::io_service& io_service,
std::future<T>& future)
{
for (;;)
{
// If the future is ready, get the value.
if (future.wait_for(std::chrono::seconds(1)) == std::future_status::ready)
{
return {future.get()};
}
// Otherwise, if the future is never going to be set, return none.
if (io_service.stopped())
{
return {boost::none};
}
}
}
...
if (auto endpoint_iterator = get(io_service, endpoint_future))
{
// use *endpoint_iterator...
}
下面是一个示例demonstrating如何在停止io_service
时安全地等待未来:
#include <chrono>
#include <iostream>
#include <thread>
#include <boost/asio.hpp>
#include <boost/asio/use_future.hpp>
#include <boost/optional.hpp>
template <typename T>
boost::optional<T> get(
boost::asio::io_service& io_service,
std::future<T>& future)
{
for (;;)
{
// If the future is ready, get the value.
if (future.wait_for(std::chrono::seconds(1)) == std::future_status::ready)
{
return {future.get()};
}
// Otherwise, if the future is never going to be set, return none.
if (io_service.stopped())
{
std::cout << "io_service stopped, future will not be set" << std::endl;
return {boost::none};
}
std::cout << "timeout waiting for future" << std::endl;
}
}
int main()
{
boost::asio::io_service io_service;
// Create I/O objects.
boost::asio::ip::udp::socket socket(io_service,
boost::asio::ip::udp::v4());
boost::asio::deadline_timer timer(io_service);
// Process the io_service in the runner thread.
auto runner = std::thread([&]() {
boost::asio::io_service::work work(io_service);
io_service.run();
});
// Start an operation that will not complete.
auto bytes_transferred_future = socket.async_receive(
boost::asio::null_buffers(), boost::asio::use_future);
// Arm the timer.
timer.expires_from_now(boost::posix_time::seconds(2));
timer.async_wait([&](const boost::system::error_code&) {
timer.cancel();
socket.close();
io_service.stop();
});
// bytes_transferred's promise will never be set as the io_service
// is not running.
auto bytes_transferred = get(io_service, bytes_transferred_future);
assert(!bytes_transferred);
runner.join();
}
答案 2 :(得分:0)
使作品在截止日期超时后可见,并允许截止时间超时删除它。
boost::scoped_ptr<boost::asio::io_service::work work;
你仍然可以在这里创建工作,但是堆分配它。
runner_ = std::thread([this]() {
work = new boost::asio::io_service::work(io_service_);
io_service_.run();
});
然后关闭:
void deadlineTimeout() {
deadline_.cancel();
socket_.close(); // socket_ is a socket on io_service_
work.reset();
// The io_service::run() will exit when there are no active requests
// (i.e. no sockeet, no deadline, and no work)
// so you do not need to call: io_service_.stop();
}