【问题标题】:Waiting with timeout on boost::asio::async_connect fails (std::future::wait_for)等待 boost::asio::async_connect 超时失败 (std::future::wait_for)
【发布时间】:2015-07-21 10:13:37
【问题描述】:

我正在使用std::futureboost::asio::async_connect,以便在发生超时时取消操作,如下所示:https://stackoverflow.com/a/30428941

但是,std::future::wait_for() 立即返回 std::future_status::deferred,即操作尚未开始。 conn_result.get() 稍后会阻塞,直到由于远程主机未在侦听而引发连接错误。我不想依赖它,因为它可能会持续很长时间,直到引发套接字错误。

如何正确等待 boost::asio 创建的期货?

编辑:SSCCE

#include <iostream>
#include <future>
#include <thread>
#include <boost/asio.hpp>
#include <boost/asio/use_future.hpp>

using boost::asio::ip::tcp;

int main(int argc, char* argv[]) {
    boost::asio::io_service ioservice;
    boost::asio::io_service::work work(ioservice);

    std::thread t([&](){ioservice.run();});

    tcp::resolver resolver(ioservice);
    tcp::resolver::query query("127.0.0.1","27015"); // random unused adress

    tcp::socket socket(ioservice);

    std::future<tcp::resolver::iterator> conn_result = boost::asio::async_connect(socket,resolver.resolve(query),boost::asio::use_future);

    std::cout << "IO Service running: " << (!ioservice.stopped() ? "y":"n") << std::endl;
    auto status = conn_result.wait_for(std::chrono::milliseconds(500));
    if (status == std::future_status::timeout) {
        socket.cancel();
        std::cout << "Timeout" << std::endl;
        return 0;
    } else if(status == std::future_status::deferred) {
        std::cout << "Deferred" << std::endl;
    }
    // If the operation failed, then conn_result.get() will throw a
    // boost::system::system_error.
    try {
        conn_result.get();
    } catch(const boost::system::system_error& e) {
        std::cerr << e.what() << std::endl;
    }


    ioservice.stop();
    t.join();

    return 0;
}
  • 编译器:MSVC2012
  • 提升 1.58

【问题讨论】:

  • std::future_status::deferred 并不意味着任务还没有开始,这意味着它不会开始,直到你显式调用get()wait() 然后它会在当前同步运行线。如果任务应该异步运行(正如async_connect 建议的那样),那么 Asio 应该使用 std::launch::async 策略调用 std::async() 以防止它被推迟。
  • @sehe 好点,已编辑。
  • @JonathanWakely 任务通常由io_service::run() 方法执行,该方法在单独的线程中运行,所以我希望这正在处理。修改后应该更清楚。 boost::asiostd::future work 的示例与我的方式相同,所以我有点困惑为什么上面的代码不起作用。
  • 在 GDB 中运行代码(使用 GCC 编译后)它运行良好,我看到 Asio 使用 std::promise not std::async` 所以应该不可能获得 deferred 状态(只能来自std::async()创建的未来)所以sehe的答案看起来是正确的,这是实现中的一个错误。

标签: c++11 boost-asio


【解决方案1】:

这似乎是 Stephan Lavavej 回答 here 的错误。

我无法找到原始错误,但它已在“RTM 版本”中修复(假设为 VS2013)。

这受内部错误号 DevDiv#255669 的影响“: wait_for()/wait_until() 不要阻止”。幸运的是,我已经收到了修复 为此,我们的一位并发运行时开发人员 Hong Hong。和我的 当前版本的 VC11,这可行:

在我当前的 VC11 版本中,这是可行的:

C:\Temp>type meow.cpp
#include <stdio.h>
#include <chrono>
#include <future>
#include <thread>
#include <windows.h>
using namespace std;

long long counter() {
    LARGE_INTEGER li;
    QueryPerformanceCounter(&li);
    return li.QuadPart;
}

long long frequency() {
    LARGE_INTEGER li;
    QueryPerformanceFrequency(&li);
    return li.QuadPart;
}

int main() {
    printf("%02d.%02d.%05d.%02d\n", _MSC_VER / 100, _MSC_VER % 100, _MSC_FULL_VER % 100000, _MSC_BUILD);

    future<int> f = async(launch::async, []() -> int {
        this_thread::sleep_for(chrono::milliseconds(250));

        for (int i = 0; i < 5; ++i) {
            printf("Lambda: %d\n", i);
            this_thread::sleep_for(chrono::seconds(2));
        }

        puts("Lambda: Returning.");
        return 1729;
    });

    for (;;) {
        const auto fs = f.wait_for(chrono::seconds(0));

        if (fs == future_status::deferred) {
            puts("Main thread: future_status::deferred (shouldn't happen, we used launch::async)");
        } else if (fs == future_status::ready) {
            puts("Main thread: future_status::ready");
            break;
        } else if (fs == future_status::timeout) {
            puts("Main thread: future_status::timeout");
        } else {
            puts("Main thread: unknown future_status (UH OH)");
        }

        this_thread::sleep_for(chrono::milliseconds(500));
    }

    const long long start = counter();

    const int n = f.get();

    const long long finish = counter();

    printf("Main thread: f.get() took %f microseconds to return %d.\n",
        (finish - start) * 1000000.0 / frequency(), n);
}


C:\Temp>cl /EHsc /nologo /W4 /MTd meow.cpp
meow.cpp

C:\Temp>meow
17.00.50419.00
Main thread: future_status::timeout
Lambda: 0
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Lambda: 1
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Lambda: 2
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Lambda: 3
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Lambda: 4
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Main thread: future_status::timeout
Lambda: Returning.
Main thread: future_status::ready
Main thread: f.get() took 2.303971 microseconds to return 1729.

我插入了计时代码来证明当 wait_for() 返回就绪时,f.get() 立即返回而不阻塞。

基本上,解决方法是在报告延迟时循环

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多