在 boost::asio::async_connect 上等待超时失败 (std::future::wait_for)

Waiting with timeout on boost::asio::async_connect fails (std::future::wait_for)

我正在使用 std::futureboost::asio::async_connect 以便在发生超时时取消操作,如此处建议:

但是,std::future::wait_for() returns 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;
}

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

我找不到原来的错误,但它已在 "the RTM version" 中修复(假设 VS2013)。

This is affected by internal bug number DevDiv#255669 ": wait_for()/wait_until() don't block". Fortunately, I've received a fix for this from one of our Concurrency Runtime developers, Hong Hong. With my current build of VC11, this works:

With my current build of VC11, this works:

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.

I inserted timing code to prove that when wait_for() returns ready, f.get() returns instantly without blocking.

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