C++20引入了协程(coroutines)概念,提供了更简洁、可读性更高的异步编程方式。与传统的线程或回调相比,协程在逻辑上接近同步代码,但在底层实现上是“暂停-恢复”机制。本文将从协程的语法、状态机实现、以及如何在项目中使用协程进行异步 IO 开发进行说明。
1. 协程的核心语法
| 关键字 | 作用 | 代码示例 |
|---|---|---|
co_await |
让协程挂起,等待 awaitable 对象完成 | auto result = co_await async_io.read(); |
co_yield |
在生成器中返回值,保持协程状态 | co_yield value; |
co_return |
结束协程,返回最终值 | co_return final_result; |
注意:协程函数的返回类型必须是 `std::future
`、`std::generator` 或自定义 `Awaitable` 类型。
2. 协程的状态机实现
C++ 编译器会把协程函数编译成一个隐式的状态机结构。关键点包括:
-
Promise 对象
promise_type负责保存协程状态、结果以及异常。get_return_object()返回的对象(如std::future)会持有对 Promise 的引用。
-
Suspend/Resume
initial_suspend()和final_suspend()控制协程启动和结束时是否挂起。await_suspend()在co_await时决定是否真正挂起。
-
栈的持久化
- 协程的局部变量被编译器拆分成成员变量,存放在协程框架的堆块中,保证协程挂起后仍能恢复。
3. 简单的协程示例:异步文件读取
#include <iostream>
#include <coroutine>
#include <future>
#include <fstream>
#include <string>
struct AwaitableRead {
std::ifstream& file;
std::string result;
bool await_ready() const noexcept { return false; }
void await_suspend(std::coroutine_handle<> h) {
// 异步读取,模拟延迟
std::thread([this, h]() {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::getline(file, result);
h.resume();
}).detach();
}
std::string await_resume() noexcept { return result; }
};
std::future<std::string> async_read_line(std::ifstream& file) {
AwaitableRead aw{file};
co_return co_await aw;
}
int main() {
std::ifstream file("example.txt");
if (!file) { std::cerr << "open fail\n"; return 1; }
auto fut = async_read_line(file);
std::cout << "Read line: " << fut.get() << '\n';
}
上述代码演示了:
await_ready判断是否需要挂起;此处总是挂起。await_suspend中启动异步任务后恢复协程。await_resume返回读取结果。
4. 与传统异步模型对比
| 特性 | 传统线程 | 回调 | 协程 |
|---|---|---|---|
| 可读性 | 低 | 低 | 高 |
| 资源占用 | 高 | 高 | 低 |
| 错误处理 | 复杂 | 复杂 | 统一(co_await 与 try/catch) |
| 调试 | 难 | 难 | 易 |
5. 在项目中使用协程的最佳实践
- 封装 Awaitable:为每种异步 I/O(网络、文件、数据库)提供对应的 Awaitable,隐藏底层实现。
- 统一异常处理:使用
try/catch包裹co_await,并通过promise_type::set_exception()传递异常。 - 避免过度切换:协程切换开销低于线程,但频繁切换仍会影响性能。
- 工具链兼容:确保编译器支持 C++20,并开启
-fcoroutines(GCC/Clang)或-std:c++latest(MSVC)。
6. 小结
C++20 的协程为异步编程提供了与同步代码相似的书写体验,降低了回调地狱与线程调度的复杂度。理解其底层状态机与 Awaitable 的实现,有助于更好地利用协程构建高性能、易维护的系统。欢迎在实际项目中尝试并反馈经验,共同探索协程的潜力。