如何用 boost::asio 等待`std::future`?

问题描述 投票:0回答:1

我有一个外部库的异步操作,我无法更改它,但我可以给它一个

callback
。我想等待它完成但又不阻塞
io_context

类似:

// Example callback-based operation
void exampleAsyncOperation(std::function<void(boost::system::error_code)> callback) {
    std::thread t([=]() {
        std::this_thread::sleep_for(std::chrono::seconds(1));
        callback(boost::system::error_code());
    });
    t.detach();
}

boost::asio::awaitable<void> NRClient::callAsyncOperation() {
    std::promise<boost::system::error_code> promise;
    auto future = promise.get_future();
    example_async_operation([&](boost::system::error_code ec) { promise.set_value(ec); });
    future.wait(); // this should be something that won't block the thread
}

或者我可以在回调中添加其他内容来获得此功能吗?

c++20 boost-asio asio
1个回答
0
投票

您可以遵循与 CompletionToken 配合使用的异步启动模式来允许任何 Asio 完成令牌,因此您可以像这样等待它

co_await asyncExample(asio::deferred); // requires recent Asio, more efficient
co_await asyncExample(asio::use_awaitable); // easier to compose with awaitable operators

这是此类实现的快速草稿:

template <typename Token> auto asyncExample(Token&& token) {
    auto init = [=](auto handler) {
        std::thread{[h = std::move(handler)]() mutable {
            std::this_thread::sleep_for(1s);

            auto ex = asio::get_associated_executor(h);
            ex.execute([h = std::move(h)]() mutable { std::move(h)(error_code{}); });
        }}.detach();
    };
    return asio::async_initiate<Token, void(error_code)>(init, token);
}

大多数(如果不是全部)棘手的部分都与您有一个非 IO 线程这一事实有关,因此必须谨慎地调用处理程序。

这是一个实时示例 Live On Coliru

  • 添加结果值,以便我们可以演示错误代码传播
  • 显示如何以元组形式重定向/接收错误代码
#include <boost/asio.hpp>
#include <iostream>
namespace asio = boost::asio;
using boost::system::error_code;
using namespace std::chrono_literals;

template <typename Token> auto asyncExample(error_code result, Token&& token) {
    auto init = [=](auto handler) {
        std::thread{[h = std::move(handler), result]() mutable {
            std::this_thread::sleep_for(1s);

            auto ex = asio::get_associated_executor(h);
            ex.execute([h = std::move(h), result]() mutable { std::move(h)(std::move(result)); });
        }}.detach();
    };
    return asio::async_initiate<Token, void(error_code)>(init, token);
}

asio::awaitable<void> callAsyncOperation() try {
    co_await asyncExample(error_code{}, asio::deferred);
    std::cout << "First completed without error" << std::endl;

    { // redirecting the error
        error_code ec;
        co_await asyncExample(make_error_code(boost::system::errc::invalid_argument),
                redirect_error(asio::deferred, ec));
        std::cout << "Redirected error: " << ec.message() << std::endl;
    }

    { // receiving as tuple
        auto [ec] = co_await asyncExample(asio::error::eof, as_tuple(asio::deferred));
        std::cout << "Received as tuple: " << ec.message() << std::endl;
    }

    // throwing by default
    co_await asyncExample(asio::error::access_denied, asio::use_awaitable);
} catch (boost::system::system_error const& se) {
    std::cerr << "Caught: " << se.code().message() << std::endl;
}

int main() {
    asio::io_context ioc;
    co_spawn(ioc, callAsyncOperation, asio::detached);
    ioc.run();
}

印刷

First completed without error
Redirected error: Invalid argument
Received as tuple: End of file
Caught: Permission denied
© www.soinside.com 2019 - 2024. All rights reserved.