在io_service.stop()之后,等待提升asio的未来将永远持续下去

时间:2016-02-26 12:35:07

标签: c++ multithreading c++11 boost boost-asio

我尝试等待从任何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()仍会阻止。在这种情况下,我怎么能停止等待未来?

3 个答案:

答案 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的值设置
  • 销毁所有I / O对象,然后销毁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();
}