boost asio和co_await来中断协程



我正在尝试使用boost asio的c++ 20协程。我目前的意图是在https://www.boost.org/doc/libs/1_75_0/doc/html/boost_asio/example/cpp17/coroutines_ts/echo_server.cpp的协程示例中嵌入一个简单的悬浮点。

根据我对这里https://en.cppreference.com/w/cpp/coroutine/suspend_always文档的理解,这个调用应该是有效的:

co_await suspend_always{};

在协程中:

awaitable<void> echo(tcp::socket socket)
{
try
{
char data[1024];
for (;;)
{
std::size_t n = co_await socket.async_read_some(boost::asio::buffer(data), use_awaitable);
co_await suspend_always{};  // <-- here
co_await async_write(socket, boost::asio::buffer(data, n), use_awaitable);
}
}
catch (std::exception& e)
{
std::printf("echo Exception: %sn", e.what());
}
}

但是,有一个编译错误:

error: no matching member function for call to 'await_transform'
co_await suspend_always{};
^~~~~~~~
谁能解释一下,如何在没有的协程中引入一个暂停点?使用定时器与async_wait.

没有"简单的悬挂点"这回事。c++协程中。挂起点是协程的内部机制,而协程提供者(具体协程库的作者)如何使用挂起是协程提供者具体实例的实现细节(在您的示例中为asio可等待)。如果协程提供程序没有公开为提供程序的协程添加用户定义扩展的API,那么你就无能为力了。

在您的示例中,您想要添加一个悬浮点。但是谁来负责恢复协同程序呢?如何恢复?这种意外的悬架制动是否符合供应商的期望?

如果不知道协程提供程序的实现细节或该协程提供程序的批准,这些操作不应该执行,这可能不是很明显,所以让我们考虑一个简单的例子——生成器。

生成器通常是这样实现的:类generator是协程的返回值。它有指向协程句柄的指针。生成器提供begin()end()成员函数,返回当前迭代器和前哨。当递增迭代器协程句柄时,继续运行并将下一个值存储在指定的存储中,或者运行到完成。当将迭代器与哨兵进行比较时,检查协程句柄是否完成(因此当协程完成时,迭代器current等于end)。当对迭代器进行解引用操作时,它将返回存储器中的存储值。

你可以使用这样的生成器:

generator example() {
co_yield 1;
co_yield 2;
co_yield 3;
}
generator g = example(); // create generator
// iterate over every value
for(auto i : g) { 
std::cout << i << 'n';
}
// prints:
// 1
// 2
// 3

然后让我们假设用户能够添加悬浮点到example函数:

generator example2() {
co_await suspend_always{}; // simple suspension point
co_yield 1;
co_yield 2;
co_yield 3;
}

很明显,现在生成器的不变量被破坏了。协程挂起,但没有存储值供迭代器返回。

这就是为什么协程提供程序需要显式支持为协程库添加扩展。

除了@Serikov提到的设计考虑之外,实现原因是:

promise_type提供成员函数await_transform

这意味着co_await awaitable中的awaitable必须经过await_transform函数,根据c++标准。

但是asio没有为std::suspend_always提供兼容的重载。看到提高/asio/impl/awaitable.hpp

我怀疑,如果你为std::suspend_always提供一个重载,它会像你期望的那样工作。

auto await_transform(std::suspend_always) noexcept { return std::suspend_always{}; }

通过检查@steven-sun提到的await_transform重载,下面的代码按预期工作。

co_await this_coro:执行者;

最新更新