C++20 引入的协程(coroutine)是对异步编程的一个重要补充,它允许函数挂起(yield)并在需要时恢复执行,从而使得异步代码更接近同步代码的可读性。本文将从协程的基本概念出发,介绍其工作机制、关键类型,并给出一个完整的示例,演示如何用协程实现一个异步文件读取器。
一、协程的基本概念
协程与普通函数的区别在于,它们可以在执行过程中挂起并恢复。挂起时,协程会把当前的执行状态(如局部变量、栈帧、指令指针)保存起来,以便后续恢复。恢复时,协程会从挂起点继续执行,像普通函数一样返回最终结果。
在 C++20 中,协程的实现基于 generator 和 task 两种主要类型:
- `generator `:类似于 Python 的 generator,能够一次返回一个 `T`,挂起时使用 `co_yield`。
- `task `:表示一个可以异步完成并最终返回 `T` 的操作,挂起时使用 `co_await`。
二、核心关键字
| 关键字 | 用途 |
|---|---|
co_await |
用于等待一个 awaitable(可以是协程、promise、future 等)。 |
co_yield |
在 generator 中返回一个值并挂起。 |
co_return |
在协程结束时返回最终值。 |
协程函数需要返回一个可等待的对象(awaitable),例如 `task
` 或 `generator`。编译器会根据返回类型的实现细节生成相应的状态机。 ## 三、实现一个异步文件读取器 下面给出一个完整示例:使用 C++20 协程从文件中按行读取内容,并将每一行异步返回给调用者。示例使用标准库中的 `std::ifstream` 和 `std::string_view`。 “`cpp #include #include #include #include #include #include namespace fs = std::filesystem; // 简易的 async generator template class async_generator { public: struct promise_type; using handle_type = std::coroutine_handle ; struct promise_type { std::optional current_value; async_generator get_return_object() { return async_generator{handle_type::from_promise(*this)}; } std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } std::suspend_always yield_value(T value) { current_value = std::move(value); return {}; } void return_void() {} void unhandled_exception() { std::terminate(); } }; async_generator(async_generator&& other) noexcept : handle(other.handle) { other.handle = nullptr; } ~async_generator() { if (handle) handle.destroy(); } bool move_next() { if (!handle) return false; handle.resume(); return !handle.done(); } T current_value() const { return *handle.promise().current_value; } private: explicit async_generator(handle_type h) : handle(h) {} handle_type handle; }; // 异步读取文件行的协程 async_generator read_lines_async(const std::string& path) { std::ifstream file(path); if (!file.is_open()) { co_return; } std::string line; while (std::getline(file, line)) { co_yield line; // 每读到一行,挂起并返回 } } // 主函数演示 int main() { const std::string file_path = “example.txt”; if (!fs::exists(file_path)) { std::cout