C++ 20 协程(三)
可等待体和等待器
promise_type
中的三个函数返回可等待体 yield_value
, initial_suspend
, final_suspend
可等待体
可等待体决定协程是否暂停
本质上,编译器使用promise和co_await操作符生成这三个函数调用。
co_await
需要一个可等待体作为参数
实现可等待体需要三个函数
C++20标准已经定义了两个基本的对象:std::suspend_always
,std::suspend_never
The Awaitable std::suspend_always
struct suspend_always {
constexpr bool await_ready() const noexcept { return false; }
constexpr void await_suspend(std::coroutine_handle<>) const noexcept {}
constexpr void await_resume() const noexcept {}
};
总是挂起,await_ready
返回false
The Awaitable std::suspend_never
struct suspend_never {
constexpr bool await_ready() const noexcept { return true; }
constexpr void await_suspend(std::coroutine_handle<>) const noexcept {}
constexpr void await_resume() const noexcept {}
};
从不挂起,await_ready
返回true
当协程协程执行的时候,这两个函数会自动执行:
- 开始
initial_suspend
- 结束
final_suspend
initial_suspend
当initial_suspend返回suspend_always时,协程会在开始时挂起;返回suspend_never时,则不会挂起
A lazy coroutine
std::suspend_always initial_suspend() {
return {};
}
A eager coroutine
std::suspend_never initial_suspend() {
return {};
}
final_suspend
在协程结束时执行,与几乎initial_suspend相同
可等待体和等待器
- 可被等待的对象称为可等待(awaitable )体或者表达式;
- co_await运算符必须返回一个等待器(awaiter):
- 可等待体和等待器可以是同一个类型;
- std::future(实验)是可等待体。
- co_await运算符返回等待器_Future_awaiter
工作流
编译器执行两个工作流外部的promise
工作流和内部的awaiter
工作流
Promise 工作流
当在函数中使用co_yield, co_await, co_return
,函数成为一个协程,并且编译器将其转换成等价的如下代码
The transformed coroutine
主要工作步骤:
- 协程开始执行:
- 申请必要的协程帧空间
- 拷贝所有函数参数到协程帧
- 创建
promise_type
对象 - 调用
promise_type
中的get_return_object
方法创建协程句柄(coroutine handle
),并保持在局部变量中,当协程第一次挂起时,将返回给调用者 - 调用
initial_suspend
并且co_await
其结果,可能返回suspend_always/never
- 当
co_await prom.initial_suspend
恢复resume
时,函数体执行
- 协程到达挂起点:
- 返回对象(prom.get_return_object())将返回给恢复协程的调用程序
- 协程到达
co_return
- 调用
prom.retrun_void/value
没有返回值或者返回值 - 销毁变量
- 调用
prom.final_suspend()
并且co_await
它的结果
- 协程销毁
- 调用
promise_type
对象和函数参数对象的析构函数 - 释放协程帧的内存
- 返还控制权给调用者
- 调用
co_await
执行等待器工作流
Awaiter工作流
调用co_await
会让编译器执行三个函数:await_ready await_suspend await_resume
The generated Awaiter Workflow
只有await_ready
返回false
时,流程才会执行,否则的话直接返回await_resume
的结果
await_ready
返回false
时:
- 协程挂起,计算
awaitable.await_suspend()
的返回值,返回值有很多种情况
出现异常情况不想写了
co_return
协程使用co_return
作为返回语句
template <typename T>
struct MyFuture
{
std::shared_ptr<T> value;
MyFuture(std::shared_ptr<T> p): value(p)
{
}
~MyFuture()
{
}
T get()
{
return *value;
}
struct promise_type
{
std::shared_ptr<T> ptr = std::make_shared<T>();
~promise_type()
{
}
MyFuture<T> get_return_object() { return ptr; }
void return_value(T v) { *ptr = v; }
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void unhandled_exception()
{
std::terminate();
}
};
};
MyFuture<int> createFuture()
{
co_return 2021;
}
int main(int argc, char* argv[])
{
auto fut = createFuture();
std::cout << fut.get() << std::endl;
}
- 流程
- 初始化协程
- 申请必要的协程帧空间
- 拷贝所有函数参数到协程帧
- 创建
promise_type
对象 - 调用
promise_type
中的get_return_object
方法将ptr
传给fut
- 调用
co_return
- 调用
return_value
并传入参数2022
- 输出
fut.get()
co_yield
无限数据流
template <typename T>
struct Generator
{
struct promise_type;
using handle_type = std::coroutine_handle<promise_type>;
Generator(handle_type h): coro(h)
{
}
handle_type coro;
~Generator() { if (coro) coro.destroy(); }
Generator(const Generator&) = delete;
Generator& operator =(const Generator&) = delete;
Generator(Generator&& oth) noexcept : coro(oth.coro)
{
oth.coro = nullptr;
}
Generator& operator =(Generator&& oth) noexcept
{
coro = oth.coro;
oth.coro = nullptr;
return *this;
}
T getValue()
{
return coro.promise().current_value;
}
bool next()
{
coro.resume();
return !coro.done();
}
struct promise_type
{
promise_type() = default;
~promise_type() = default;
auto initial_suspend()
{
return std::suspend_always{};
}
auto final_suspend() noexcept
{
return std::suspend_always{};
}
auto get_return_object()
{
return Generator{handle_type::from_promise(*this)};
}
auto return_void()
{
return std::suspend_never{};
}
auto yield_value(const T value)
{
current_value = value;
return std::suspend_always{};
}
void unhandled_exception()
{
std::terminate();
}
T current_value;
};
};
Generator<int> getNext(int start = 0, int step = 1)
{
auto value = start;
while (true)
{
co_yield value;
value += step;
}
}
int main(int argc, char* argv[])
{
auto gen = getNext();
for (int i = 0; i <= 10; ++i)
{
gen.next();
std::cout << std::format("gen value: {}\n", gen.getValue());
}
std::cout << "\n\n";
auto gen2 = getNext(100, -10);
for (int i = 0; i <= 20; ++i)
{
gen2.next();
std::cout << std::format("gen2 value: {}\n", gen2.getValue());
}
}
看一下执行流程;
- 创建
promise_type
- 调用
get_return_object()
,将其结果保存在局部变量 - 创建
generator
- 调用
initial_suspend
挂起协程 - 请求下一个值并消耗一个值然后挂起
- 接着调用
gen.next
重复循环