当年我们写 C。
想一边算一边产出。
通常只能用回调。
或者把结果全塞进数组。
再一次性返回。
你要么写得绕。
要么吃内存。
当年:想“流式产出”,只能手搓状态机
你想写一个遍历器。
比如读日志。
一行一行吐出来。
你会写一个 while。
读一行。
处理一行。
这当然能跑。
但如果你想把“读行”封装成一个可复用组件。
你就开始写状态。
写 next()。
写内部缓存。
写到最后。
你发现你在手搓一个生成器。
协程给了一个词:generator
generator 的意思很朴素。
函数可以在中间 co_yield。
把一个值吐出去。
下次继续。
从上次停的地方接着跑。
一个最小 generator:先看用法
先看你真正想写的东西。
这里的 Generator。
是一个示意名字。
标准库没有官方的 std::generator。
你可以把它理解成。
“你用的协程库里那个生成器类型”。
Generator<int> evens(int n) {
for (int i = 0; i < n; ++i) {
if (i % 2 == 0) co_yield i;
}
}
这段代码像人话。
像一个普通函数。
只是多了 co_yield。
generator 到底靠什么运作
你不需要现在就手写一个完整的 generator。
那玩意很容易写成一坨。
你只要抓住两条就够了。
第一。
co_yield value 会落到 promise 的 yield_value(value)。
第二。
每次迭代 ++,底层都会做一次 resume()。
你可以把它想成。
for-range 在背后帮你循环“叫醒”协程。
// 伪代码:每次 ++ 就恢复一次协程
it.operator++() { h.resume(); }
task:另一种协程形态
task 更像“异步函数”。
同样。
Task 也只是示意名。
它可能来自你们的协程 runtime。
也可能来自某个第三方库。
你 co_await 等待。
最后 co_return 一个结果。
它的使用方式更像。
“我等一个 future”。
Task<int> get_user_id();
Task<void> work() {
int id = co_await get_user_id();
(void)id;
co_return;
}
关键结论
generator 是“我产出一串值”。
task 是“我最终给你一个结果”。
别混。
混了你就会写出很怪的接口。
小结
你可以把 generator 当成“可暂停的 for 循环”。
把 task 当成“可暂停的函数调用”。
它们用的都是协程。
但心智模型完全不同。