将 C++20 协程与 Boost.Asio 结合使用时,可以使用
boost::asio::use_awaitable
或 boost::asio::deferred
使操作等待,如以下示例所示。
std::size_t n = co_await my_socket.async_read_some(buffer, use_awaitable);
std::size_t n = co_await my_socket.async_read_some(buffer, deferred);
有什么区别?在什么情况下您会使用其中一种而不是另一种?
Deferred 与协程无关。它将任何异步启动打包到新的启动函数中,该函数可以接受任何完成令牌。 文档中显示了延迟启动的玩具实现示例。
更明确的使用方式是拼写:
auto d = co_await my_socket.async_read_some(buffer, deferred);
std::size_t n = co_await d(asio::use_awaitable);
我承认我不知道 Asio 的可等待框架(
promise_type
)为异步操作实现了await_transform
,但是它确实,这就是简写的原因
std::size_t n = co_await d;
有效。
其他注意事项:
Deferred 可以与不同的完成令牌一起使用,例如定期回调或
use_future
延迟的异步操作是可复制的
直到几个版本之前,延迟异步操作都是可重用的,所以
n = co_await d;
n = co_await d;
会起作用的。在我下面的测试中,我发现这最近发生了变化,所以你需要
auto d2 = d;
n = co_await std::move(d);
n = co_await std::move(d2);
达到同样的效果。
以下代码演示了其中一些概念的实际应用:
#include <any>
#include <iostream>
#include <mutex>
#include <sstream>
#include <string>
#include <thread>
#include <vector>
#include <boost/asio.hpp>
namespace asio = boost::asio;
using asio::ip::tcp;
using boost::system::error_code;
asio::awaitable<void> foo() try {
tcp::socket my_socket{co_await asio::this_coro::executor};
co_await my_socket.async_connect({{}, 7878}, asio::use_awaitable);
std::array<char, 16> b;
{
asio::awaitable<size_t> aw = my_socket.async_read_some(asio::buffer(b), asio::use_awaitable);
size_t n = co_await std::move(aw);
std::cout << __LINE__ << ": Read: " << n << " bytes" << std::endl;
}
{
/*asio::deferred_async_operation<void(error_code, size_t), tcp::socket::initiate_async_receive,
asio::mutable_buffers_1 const&, int>*/
auto d1 = my_socket.async_read_some(asio::buffer(b), asio::deferred);
auto d2 = d1;
size_t n = co_await d1(asio::use_awaitable);
std::cout << __LINE__ << ": Read: " << n << " bytes" << std::endl;
n = co_await std::move(d2);
std::cout << __LINE__ << ": Read: " << n << " bytes" << std::endl;
}
} catch(boost::system::system_error const& se) {
std::cout << __LINE__ << ": " << se.code().message() << std::endl;
}
int main() {
asio::io_context ioc;
co_spawn(ioc, foo, asio::detached);
ioc.run();
}
演示输出: