1. 为什么C++20协程值得关注
三年前我第一次在生产环境尝试使用协程重构网络服务时,手动实现了近2000行的调度器和状态机代码。当看到C++20将协程纳入标准的那天,我对着编译器更新日志足足发了十分钟呆——这意味着我们终于可以扔掉那些脆弱的第三方协程库,在语言层面获得可靠的异步编程支持。
协程(coroutine)本质上是一种可暂停和恢复的函数,这种特性使得我们可以用同步代码的书写方式实现异步逻辑。在C++20之前,要实现类似功能要么依赖平台特定的API(如Windows纤程),要么使用第三方库(如Boost.Coroutine2)。现在通过标准化的co_await/co_yield关键字,配合编译器生成的状态机代码,开发者能够以更低的认知成本处理IO密集型任务。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 协程核心机制拆解
2.1 编译器背后的魔法
当函数体内出现co_await或co_yield时,编译器会进行如下转换:
- 将函数返回值包装为
std::coroutine_handle - 自动生成包含以下成员的promise对象:
initial_suspend():控制协程启动时是否立即暂停final_suspend():控制协程结束时是否暂停return_void()/return_value():处理返回值unhandled_exception():异常处理
cpp复制// 原始代码
task<int> foo() {
co_return 42;
}
// 编译器生成伪代码
void foo(coroutine_frame* frame) {
try {
frame->promise.return_value(42);
} catch(...) {
frame->promise.unhandled_exception();
}
}
2.2 关键组件交互流程
一个完整的协程调用涉及三个核心对象:
- 协程句柄:
std::coroutine_handle,用于恢复协程执行 - 承诺对象:实现特定接口的定制点
- 返回对象:通常包含awaitable类型
典型生命周期:
mermaid复制graph TD
A[创建协程帧] --> B[调用initial_suspend]
B -->|返回suspend_always| C[暂停]
C --> D[调用返回对象的await_transform]
D --> E[co_await表达式求值]
E --> F[恢复执行]
F --> G[co_return或结束]
G --> H[调用final_suspend]
3. 实现自定义协程类型
3.1 设计可等待对象(Awaitable)
实现awaitable需要三个关键方法:
cpp复制struct my_awaitable {
bool await_ready() noexcept;
void await_suspend(coroutine_handle<> h) noexcept;
auto await_resume() noexcept;
};
实际案例:实现线程切换器
cpp复制struct switch_to_new_thread {
bool await_ready() { return false; } // 总是暂停
void await_suspend(coroutine_handle<> h) {
std::thread([h] { h.resume(); }).detach();
}
void await_resume() {}
};
task<void> foo() {
co_await switch_to_new_thread{}; // 切换到新线程执行
// 此处已在新线程上下文
}
3.2 构建协程返回类型
完整的task实现需要:
- 定义promise_type
- 实现协程句柄管理
