C++20中的协程:从概念到实践

在C++20标准发布后,协程(coroutines)成为了语言层面的一个重要新特性。它们为编写高效、可读的异步代码提供了一种更直观、更接近同步写法的方式。本文将从协程的基本概念、核心语法、编译器实现以及典型应用场景展开讨论,并给出一段完整的示例代码,帮助读者快速上手。

1. 协程的基本概念

协程是一种轻量级的用户级线程,能够在执行过程中暂停(yield)并恢复(resume),而不是像线程那样由操作系统调度。协程通过在函数内部插入 co_yield, co_await, co_return 三个关键词实现非阻塞的控制流。

  • co_yield 用于返回一个值给调用者,并暂停协程的执行。
  • co_await 用于等待一个可等待对象(awaitable),协程会在此处挂起。
  • co_return 用于返回协程的最终结果,并结束协程。

与传统的回调或 promise 机制相比,协程的写法更接近同步代码,错误处理也更简洁。

2. 核心语法与编译器实现

C++20 并未指定协程的具体实现细节,而是提供了标准库的协程相关头文件 `

`。编译器(如 GCC 10+, Clang 10+, MSVC 19.27+)在生成协程时会隐式生成一个 **promise** 对象,用来管理协程的状态和结果。 下面给出一个最小的协程函数签名示例: “`cpp #include #include #include struct Generator { struct promise_type { std::string current_value; std::string get_return_object() { return Generator{std::coroutine_handle ::from_promise(*this)}; } std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } std::suspend_always yield_value(std::string value) { current_value = std::move(value); return {}; } void unhandled_exception() { std::terminate(); } void return_void() {} }; std::coroutine_handle handle; explicit Generator(std::coroutine_handle h) : handle(h) {} ~Generator() { if (handle) handle.destroy(); } bool next() { if (!handle.done()) { handle.resume(); return true; } return false; } const std::string& value() const { return handle.promise().current_value; } }; Generator my_words() { co_yield “Hello”; co_yield “from”; co_yield “C++20”; } “` 在上面的代码中,`Generator` 充当了一个可迭代的协程容器,调用者可以通过 `next()` 来逐个获取产生的字符串。 ## 3. 协程的典型应用场景 1. **异步 I/O**:利用 `co_await` 与异步库(如 Boost.Asio、cppcoro)配合,简化网络编程。 2. **流式数据处理**:如文件行读取、视频帧解码等,使用 `co_yield` 逐帧生成。 3. **协程调度器**:实现轻量级任务调度,替代传统线程池。 4. **状态机**:通过协程实现复杂状态转换,代码更易维护。 ## 4. 完整示例:协程实现异步文件读取 下面给出一个基于 cppcoro 的异步文件读取示例。由于示例环境不一定支持 cppcoro,代码仅供参考。 “`cpp #include #include #include #include cppcoro::task read_file_async(asio::io_context& io_context, std::string filename) { asio::async_file file(io_context, filename, asio::async_file::mode_read); std::vector buffer(1024); std::size_t bytes_read = 0; try { while (true) { std::size_t n = co_await file.async_read_some(asio::buffer(buffer), asio::use_awaitable); if (n == 0) break; // EOF bytes_read += n; std::cout.write(buffer.data(), n); } std::cout **说明**:此示例使用 `cppcoro` 的 `task` 作为协程返回类型,并通过 `asio::use_awaitable` 将异步操作适配为协程可等待对象。 ## 5. 编写协程时的注意事项 – **异常传播**:协程内部抛出的异常会被包装到 promise 的 `unhandled_exception`,如果没有自定义处理,默认会调用 `std::terminate`。 – **资源管理**:协程的内部状态会存储在堆上,使用完毕后一定要 `destroy`,否则会导致内存泄漏。 – **性能开销**:虽然协程比线程轻量,但在频繁创建/销毁协程时仍会产生一定开销。 – **可等待对象**:自定义 awaitable 对象时,需要实现 `await_ready`, `await_suspend`, `await_resume` 三个成员函数。 ## 6. 小结 C++20 协程为语言带来了新的异步编程模型,使得原本需要回调、promise 或线程的复杂逻辑可以更直观地写成同步样式。掌握协程的基本语法与实现原理后,读者可以将其应用于网络、I/O、游戏循环等多种场景,显著提升代码可读性与维护性。希望本文能为你踏入 C++ 协程的世界提供一份清晰的起点。

发表评论