如何在 C++20 中实现协程(Coroutines)

C++20 在标准库中正式引入协程(Coroutines)这一强大的语言特性,允许我们以同步的写法处理异步和懒惰计算。下面从协程的基本概念、关键字、实现原理以及一个完整示例三部分,系统阐述如何在 C++20 中使用协程。

1. 协程基础

协程是一种可挂起的函数,能够在执行过程中暂停(co_awaitco_yieldco_return)并在之后恢复。与线程不同,协程不涉及系统上下文切换,协程之间的状态在栈上保存,效率更高。

关键语法:

  • co_await expr:等待 expr 产生结果,暂停协程。
  • co_yield expr:产生一个值给调用方,协程挂起,稍后恢复。
  • co_return expr:返回最终结果,结束协程。

2. 关键字与约束

关键字 用途 约束
co_await 等待 awaitable 对象 awaitable 必须实现 operator co_await 或满足 await_ready, await_suspend, await_resume 三个成员函数
co_yield 产生一个值 需要一个 generator(生成器)返回类型
co_return 结束协程 可返回值或无值

协程函数的返回类型必须满足“协程返回类型”约束,通常使用 `std::future

`、`std::generator` 或自定义类型。 ## 3. 实现原理概览 1. **协程入口**:编译器会把协程函数展开为一个状态机,状态保存在“协程帧”中。协程帧在堆上(或可以在栈上实现)分配,并存储所有局部变量、参数以及当前状态。 2. **挂起与恢复**:`co_await`/`co_yield` 触发挂起时,协程帧会记录当前位置并返回给调用方;恢复时,协程帧从上次保存的位置继续执行。 3. **awaitable 对象**:协程通过 `await_ready` 判断是否立即完成;若不立即完成,则调用 `await_suspend` 让调度器挂起协程,直到完成后调用 `await_resume` 取值。 ## 4. 一个完整示例:异步文件读取 下面展示一个利用 C++20 协程实现异步文件读取的完整程序。我们使用 `std::experimental::generator`(或在 C++20 标准中使用 `std::generator`,但目前尚未正式发布)来生成读取块,使用 `std::future` 处理最终结果。 “`cpp #include #include #include #include #include #include #include using namespace std::literals::chrono_literals; // 简单的 awaitable,包装 std::future template struct FutureAwaitable { std::future fut; bool await_ready() const noexcept { return fut.wait_for(0s) == std::future_status::ready; } void await_suspend(std::coroutine_handle h) noexcept { std::thread([h, fut = std::move(fut)]() mutable { fut.get(); // 让 future 完成 h.resume(); // 恢复协程 }).detach(); } T await_resume() noexcept { return fut.get(); } }; // 读取文件块的协程生成器 struct FileBlockGenerator { struct promise_type { FileBlockGenerator get_return_object() { return FileBlockGenerator{ std::coroutine_handle ::from_promise(*this) }; } std::suspend_never initial_suspend() noexcept { return {}; } std::suspend_never final_suspend() noexcept { return {}; } void return_void() noexcept {} void unhandled_exception() { std::terminate(); } }; std::coroutine_handle coro; FileBlockGenerator(std::coroutine_handle h) : coro(h) {} ~FileBlockGenerator() { if (coro) coro.destroy(); } bool next() { if (!coro.done()) { coro.resume(); return !coro.done(); } return false; } std::vector value; }; FileBlockGenerator read_file_in_chunks(const std::string& path, std::size_t chunk_size = 4096) { std::ifstream file(path, std::ios::binary); if (!file) co_return; while (file) { std::vector buffer(chunk_size); file.read(buffer.data(), chunk_size); std::size_t read_bytes = file.gcount(); if (read_bytes == 0) break; buffer.resize(read_bytes); co_yield buffer; } } // 主协程:异步读取文件并统计字节数 auto async_file_reader(const std::string& path) -> std::future { struct Awaitable { std::coroutine_handle h; Awaitable(std::coroutine_handle handle) : h(handle) {} bool await_ready() const noexcept { return false; } void await_suspend(std::coroutine_handle awaiting) noexcept { h.resume(); // 直接恢复主协程 } std::size_t await_resume() noexcept { return h.promise().result; } }; struct Promise { std::size_t result = 0; auto get_return_object() { return std::future{std::async(std::launch::deferred, [&]() { return result; })}; } std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() { return {}; } void return_void() {} void unhandled_exception() { std::terminate(); } }; struct Coroutine { std::coroutine_handle coro; Coroutine(std::coroutine_handle h) : coro(h) {} ~Coroutine() { if (coro) coro.destroy(); } }; return Coroutine::Awaitable{[]() -> std::future { std::size_t total = 0; for (auto chunk : read_file_in_chunks(path)) { total += chunk.size(); } co_return total; }()}; } // 示例使用 int main() { std::string path = “example.txt”; auto fut = async_file_reader(path); std::size_t size = fut.get(); std::cout `。这里用 `std::async` 简化示例,真实项目可以使用更完整的调度器。 4. **协程帧**:所有局部变量(`buffer`, `total` 等)都保存在协程帧中,保证挂起后恢复时保持状态。 ## 5. 进一步阅读 – 《C++20 标准草案》相关章节(协程、awaitable、generator)。 – 《C++协程实战》:深入讨论协程调度器、线程池、IO 事件等。 – Boost.Coroutine / cppcoro:社区提供的协程工具库,适合更复杂的场景。 通过上述示例,你可以看到 C++20 协程为异步编程提供了简洁、类型安全且高性能的手段。只要掌握了 `co_await`, `co_yield`, `co_return` 以及 awaitable 的实现规则,你就能在自己的项目中轻松构建高效的协程结构。

发表评论