C++20 协程 — Coroutines
三大关键字
co_await expr // 暂停,等待异步操作完成
co_yield expr // 暂停,产出一个值(generator)
co_return expr // 结束协程,返回最终值
任何含这三个关键字之一的函数就是协程——返回值必须是满足协程约束的类型。
最简单的 Generator
#include <coroutine>
#include <iostream>
template <typename T>
struct Generator {
struct promise_type {
T current;
Generator get_return_object() {
return Generator{Handle::from_promise(*this)};
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
std::suspend_always yield_value(T v) { current = v; return {}; }
void return_void() {}
void unhandled_exception() { std::terminate(); }
};
using Handle = std::coroutine_handle<promise_type>;
Handle h;
explicit Generator(Handle h) : h(h) {}
~Generator() { if (h) h.destroy(); }
Generator(const Generator &) = delete;
Generator(Generator &&o) : h(o.h) { o.h = nullptr; }
bool next() {
if (!h || h.done()) return false;
h.resume();
return !h.done();
}
T value() { return h.promise().current; }
};
// 使用
Generator<int> range(int n) {
for (int i = 0; i < n; ++i)
co_yield i; // 暂停,产出 i,等 next() 再恢复
}
int main() {
auto gen = range(5);
while (gen.next())
std::cout << gen.value() << ' '; // 0 1 2 3 4
}
协程的生命周期
1. 分配协程帧(堆上,编译器可能优化到栈)
2. 调用 promise_type::get_return_object() → 返回给调用者
3. initial_suspend → 暂停还是立即执行
4. 协程体执行(co_await / co_yield / co_return)
5. final_suspend → 暂停等待销毁或自动销毁
6. promise 析构 + 协程帧释放
简单 Task(异步替代 future)
struct Task {
struct promise_type {
Task get_return_object() { return Task{Handle::from_promise(*this)}; }
std::suspend_never initial_suspend() { return {}; } // 立即开始
std::suspend_always final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() { std::terminate(); }
};
using Handle = std::coroutine_handle<promise_type>;
Handle h;
~Task() { if (h) h.destroy(); }
bool done() { return h.done(); }
void resume() { h.resume(); }
};
Task asyncWork() {
std::cout << "Step 1\n";
co_await std::suspend_always{}; // 暂停
std::cout << "Step 2\n";
}
int main() {
auto task = asyncWork(); // Step 1(initial_suspend_never 立即执行)
std::cout << "Main says hi\n";
task.resume(); // Step 2
}
// 输出: Step 1 → Main says hi → Step 2
协程的实际价值
// ❌ 传统异步 — 回调地狱
fetchData([this](Data d) {
processData(d, [this](Result r) {
saveResult(r, [this]() {
updateUI();
});
});
});
// ✅ 协程 — 像同步代码一样写异步逻辑
Task loadAndProcess() {
Data d = co_await fetchData();
Result r = co_await processData(d);
co_await saveResult(r);
updateUI();
}
关键概念
| 概念 | 说明 |
|---|---|
promise_type | 协程内部状态,控制协程行为 |
coroutine_handle | 协程的外部句柄,可以 resume/destroy |
initial_suspend | 刚进协程时的行为(立即开始 vs 暂停等待) |
final_suspend | 结束时的行为(自动销毁 vs 让外部手动销毁) |
| 协程帧 | 存放局部变量和 promise 的堆内存 |
常见陷阱
// 陷阱 1: 协程帧在堆上分配 → 可能引发 malloc
// 编译器有时能优化(HALO — Heap Allocation eLision Optimization)
// 但不保证 → 高频调用要测试
// 陷阱 2: 引用参数可能悬空
Task bad(const std::string &s) {
co_await something();
std::cout << s; // s 可能已析构!
}
// ✅ 按值传参或确保生命周期
// 陷阱 3: co_await 只能在协程内使用
// co_await expr 只能在 async 函数中