我有一个外部库的异步操作,我无法更改它,但我可以给它一个
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
}
或者我可以在回调中添加其他内容来获得此功能吗?
您可以遵循与 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