C++20 在标准库中正式加入了协程(coroutine)支持,极大地简化了异步编程和生成器的实现。本文将从协程的基本概念、实现原理、关键标准库组件以及一个实际案例三个部分,系统阐述协程在 C++20 中的工作方式,并展示如何在项目中使用它们来提升代码可读性和性能。
一、协程的基本概念
协程是一种比线程更轻量级的计算单元,能够在多个点暂停和恢复执行。与传统的函数调用不同,协程可以在执行期间挂起(co_await、co_yield、co_return),并在需要时再次恢复。协程的本质是一个状态机:当协程被挂起时,其局部状态(局部变量、栈帧等)被保存在协程句柄所指向的堆对象中。
协程的三种核心操作:
- co_await:等待一个 awaitable 对象完成后继续执行。
- co_yield:产生一个值并挂起协程,等待下次调用。
- co_return:终止协程并返回最终结果。
二、实现原理
1. 协程句柄与协程类型
C++20 引入了 `std::coroutine_handle
`,用于控制协程的生命周期。协程句柄内部包含: – **状态机**:通过 Promise 类型的 `get_return_object()` 返回值来创建。 – **悬挂点**:当协程挂起时,句柄可用于恢复。 ### 2. Promise 对象 每个协程都有一个对应的 Promise 对象,用于: – **保存状态**:局部变量、异常、返回值等。 – **管理生命周期**:实现 `initial_suspend`、`final_suspend`、`return_value`、`unhandled_exception` 等钩子。 ### 3. 编译器实现 编译器把协程转换为: – 生成一个结构体来保存协程状态。 – 在 `operator co_await`、`operator co_yield` 等处插入状态机跳转逻辑。 ## 三、关键标准库组件 | 组件 | 作用 | 示例 | |——|——|——| | `std::coroutine_handle` | 句柄,控制协程 | `auto h = coro.get_handle(); h.resume();` | | `std::suspend_always` / `std::suspend_never` | 决定挂起/不挂起 | `co_await std::suspend_always();` | | `std::generator`(在 C++23 中) | 生成器 | `for (auto v : gen) { … }` | | `std::future` | 异步结果 | `auto fut = async_operation(); fut.get();` | | `std::async` | 兼容协程的异步执行 | `auto fut = std::async(std::launch::async, []{ … });` | ## 四、实战案例:异步文件读取 下面用协程实现一个异步读取文件的例子,使用 C++20 标准库中的 `std::filesystem` 与 `std::async` 结合 `co_await`。 “`cpp #include #include #include #include #include #include using namespace std::literals; struct async_file_reader { struct promise_type { std::string result; std::string file_name; async_file_reader get_return_object() { return async_file_reader{std::coroutine_handle ::from_promise(*this)}; } std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } void unhandled_exception() { std::terminate(); } void return_value(std::string&& val) { result = std::move(val); } }; std::coroutine_handle handle; async_file_reader(std::coroutine_handle h) : handle(h) {} ~async_file_reader() { if (handle) handle.destroy(); } std::string get() { return std::move(handle.promise().result); } }; async_file_reader read_file_async(const std::string& path) { // 模拟耗时 IO co_await std::suspend_always{}; std::ifstream ifs(path, std::ios::binary | std::ios::ate); std::ifstream::pos_type pos = ifs.tellg(); std::string result(pos, ‘\0’); ifs.seekg(0, std::ios::beg); ifs.read(&result[0], pos); co_return std::move(result); } int main() { auto reader = read_file_async(“example.txt”); // 主线程可以执行其他工作 std::cout << "正在读取文件…\n"; // 恢复协程 reader.handle.resume(); std::cout << "文件内容长度: " << reader.get().size() << '\n'; } “` **解释**: 1. `read_file_async` 是一个协程,返回 `async_file_reader`。 2. `co_await std::suspend_always{}` 用于模拟挂起点,实际应用中可以挂起到异步 I/O 事件完成。 3. `co_return` 把文件内容传递给 Promise。 4. 在 `main` 中,我们先创建协程,随后可以做其他工作,最后通过 `handle.resume()` 恢复协程,读取结果。 ## 五、总结 C++20 的协程为异步编程提供了更直观、更高效的解决方案。通过理解协程句柄、Promise 对象以及编译器生成的状态机,我们可以轻松编写生成器、异步 I/O、协程池等高级功能。建议在项目中先实现小型协程示例,逐步扩展到完整的异步框架。随着标准库的进一步完善(如 C++23 的 `std::generator`、`std::expected`),协程将在 C++ 生态中发挥更大作用。