本章目标:理解协程的核心概念、用生成器模式体验 co_yield、理解 co_await 与异步模型、了解标准库现状。 协程是 C++20 最有野心也最"不完整"的特性——先懂思想。
协程 = 可以"暂停/恢复"的函数。 普通函数:调用 → 一路跑到 return,无法中途停。 协程:可以执行到某个点"挂起",把控制权交还调用者, 将来某时刻"从挂起点继续"。
三个新关键字:
co_await 挂起,等待某个异步操作完成
co_yield 产出一个值,然后挂起(生成器)
co_return 结束协程
代价与门槛:
(promise_type 等)——这部分又长又绕
工程上请用现成库(asio、cppcoro、或 C++26 演进)
// 一个"惰性产出"的生成器:不一次性算完,用多少算多少
Generator<int> range(int from, int to) {
for (int i = from; i < to; ++i)
co_yield i; // 产出 i 并挂起
}
for (int x : range(0, 5)) // 每次循环"唤醒"一次
std::println("{}", x); // 打印 0 1 2 3 4
对比普通函数返回 vector:
C++20 协程需要一个"promise 类型"(编译器约定的接口):
template <typename T>
class Generator {
public:
struct promise_type; // 编译器要求的名字
using handle = std::coroutine_handle<promise_type>;
struct promise_type {
T current;
struct FinalAwaiter { // 结束时挂起
bool await_ready() const noexcept { return false; }
void await_suspend(handle) const noexcept {}
void await_resume() const noexcept {}
};
Generator get_return_object() { return Generator{handle::from_promise(*this)}; }
std::suspend_always initial_suspend() const noexcept { return {}; }
FinalAwaiter final_suspend() const noexcept { return {}; }
void return_void() const noexcept {}
void unhandled_exception() { std::terminate(); } // 教材简化
std::suspend_always yield_value(T value) {
current = std::move(value); // 存下产出的值
return {}; // 挂起
}
};
struct Iterator {
handle h;
bool operator!=(const Iterator&) const noexcept { return !h.done(); }
void operator++() { h.resume(); } // 唤醒协程
T& operator*() { return h.promise().current; }
};
Iterator begin() { h.resume(); return {h}; }
Iterator end() { return {handle{} }; }
Generator(Generator&& o) noexcept : h(o.h) { o.h = nullptr; }
~Generator() { if (h) h.destroy(); }
private:
explicit Generator(handle h) : h(h) {}
handle h;
};
这是"能跑"的最小版本,理解即可。工程上:
Generator<unsigned long long> fibonacci() {
unsigned long long a = 0, b = 1;
while (true) {
co_yield a; // 无限序列!
unsigned long long t = a + b;
a = b; b = t;
}
}
int count = 0;
for (auto v : fibonacci()) {
if (count++ >= 10) break; // 用多少算多少
std::println("{}", v);
}
普通函数做不到"返回无限序列"(内存会爆炸), 生成器可以——这就是惰性求值的力量。
// 概念示意(需要异步框架,如 asio)
auto result = co_await async_read(file); // 挂起,数据到了再继续
执行流程:
好处:异步代码写成"顺序"样子,没有回调地狱。
C++20 只给了机制(awaiter 接口), 标准库的"官方异步 IO"仍未完成; 实际项目用 asio 等库。现在只需要懂概念。
开销约几十纳秒~几百纳秒(比线程切换快 10~100 倍)
练习题(配套测试:测试_第29章_协程.cpp)