第一次看协程。
你大概率会卡在这句。
“它到底停在哪。”
更卡的是。
“它停了以后谁来叫醒它。”
别急。
把协程当成一套协议就行。
协议有三份。
当年:我们用回调在做“手工调度”
回调里。
你其实一直在做两件事。
第一。
把当前函数剩下的逻辑。
塞进一个 lambda。
第二。
把这个 lambda 交给别人。
让别人“以后再调用”。
协程只是把这两件事。
标准化了。
三个角色
promise_type
它属于协程返回类型。
你可以把它理解成。
“协程和调用者的合同”。
协程返回什么。
异常怎么处理。
结束时怎么清理。
都在这里。
awaiter
它属于等待动作。
你可以把它理解成。
“我怎么挂起、怎么恢复”。
awaitable
它是“能被 co_await 的东西”。
它可能本身就是 awaiter。
也可能通过 operator co_await 变成 awaiter。
先看最小 promise_type:协程到底怎么返回
#include <coroutine>
struct Task {
struct promise_type {
Task get_return_object() { return {}; }
std::suspend_never initial_suspend() noexcept { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void return_void() noexcept {}
void unhandled_exception() { std::terminate(); }
};
};
你先记住三个点。
get_return_object:把协程句柄打包成返回值。
initial_suspend:一开始要不要先挂起。
final_suspend:结束时要不要挂起,让外部收尾。
再看最小 awaiter:挂起与恢复
一个 awaiter 只要满足三个函数。
#include <coroutine>
struct ManualResume {
std::coroutine_handle<> h;
bool await_ready() const noexcept { return false; }
void await_suspend(std::coroutine_handle<> handle) noexcept { h = handle; }
void await_resume() const noexcept {}
};
await_suspend 会拿到当前协程的 handle。
你把它存起来。
以后想恢复。
就 h.resume()。
一个能跑的最小例子:我手动叫醒协程
#include <iostream>
ManualResume g;
Task f() {
std::cout << "A\n";
co_await g;
std::cout << "B\n";
}
int main() {
f();
std::cout << "wake\n";
g.h.resume();
}
输出会是。
A。
wake。
B。
你看到的不是多线程。
你看到的是。
函数能在中间停住。
然后从停住的地方继续。
关键结论
协程本质上。
就是把“剩下的逻辑”保存起来。
再把“什么时候继续”交给 awaiter。
小结
promise_type 负责协程的生命周期和返回。
awaiter 负责挂起与恢复。
awaitable 负责把 awaiter 提供出来。
你把这三件事想清楚。
协程就不再神秘。