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

协程(Coroutines)是 C++20 引入的强大语言特性,它们让函数可以在执行过程中挂起并在后续恢复,从而实现异步编程、生成器、事件循环等多种模式。本文将从协程的基本概念入手,逐步演示如何在 C++ 中定义、使用并优化协程。

1. 协程的基本语义

协程本质上是一个可以被多次暂停和恢复的函数。与普通函数不同,协程会在执行到 co_awaitco_yieldco_return 时挂起,而不是像线程那样切换到另一条执行流。

关键关键词

  • co_await:挂起当前协程并等待一个 awaitable 对象完成。
  • co_yield:将一个值返回给调用者,协程挂起,稍后恢复。
  • co_return:结束协程并返回一个值(如果有返回类型)。

awaitable 对象

一个对象可以被 co_await,只需满足以下两条规则:

  1. operator co_await() 返回一个类型为 Awaiter 的对象。
  2. Awaiter 必须实现 await_ready(), await_suspend(), await_resume() 三个成员函数。

标准库提供了许多 ready-made awaitable,如 std::suspend_always, std::suspend_never,以及 std::experimental::coroutine_handle.

2. 协程的返回类型

协程函数必须返回 std::futurestd::promisestd::generator(实验性)或自定义类型。最常见的是 `std::future

`。如果你想实现一个生成器,C++20 里有 `std::generator`(需要包含 “),但在标准库实现还不完全稳定。 示例: “`cpp #include #include #include struct Task { struct promise_type { Task get_return_object() { return {}; } std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } void return_void() {} void unhandled_exception() { std::terminate(); } }; }; Task simple_coroutine() { std::cout #include #include namespace asio = boost::asio; // 或者使用 std::asio using asio::awaitable; using asio::co_spawn; using asio::detached; using asio::use_awaitable; awaitable async_read_file(const std::string& path) { asio::io_context& ctx = co_await asio::this_coro::executor; asio::posix::stream_descriptor file(ctx); std::error_code ec; // 打开文件 file.assign(open(path.c_str(), O_RDONLY), ec); if (ec) { throw std::system_error(ec); } // 读取文件内容 std::vector buffer(1024); std::size_t total = 0; for (;;) { std::size_t n = co_await file.async_read_some(asio::buffer(buffer), use_awaitable); if (n == 0) break; // EOF total += n; // 处理 buffer 前 n 个字节 } file.close(); co_return total; } int main() { asio::io_context ctx; co_spawn(ctx, async_read_file(“test.txt”), [](std::exception_ptr ep) { if (ep) std::rethrow_exception(ep); }); ctx.run(); } “` ### 说明 – `awaitable ` 是一个封装好的协程返回类型,使用 `asio::use_awaitable` 作为完成句柄。 – `co_spawn` 用来在 `io_context` 上执行协程。 – `co_await` 让协程挂起,等待异步 I/O 完成。 ## 4. 性能注意事项 1. **堆栈大小**:协程的挂起点会保存局部状态,通常会在堆上分配栈帧。过多的协程可能导致内存碎片,需要使用 `std::stack` 或 `asio::execution_context` 自定义堆栈管理。 2. **协程对象的拷贝**:默认情况下,协程返回对象是不可移动的,需自定义 `promise_type` 的 `get_return_object` 以返回可移动对象。 3. **异常传播**:在协程中使用 `try/catch` 捕获异常并在 `promise_type` 中通过 `unhandled_exception()` 处理,确保异常能正确传递。 ## 5. 小结 – 协程是 C++20 的重要特性,适用于异步编程、生成器和事件驱动模型。 – 必须使用 `co_await`、`co_yield`、`co_return` 关键字,并返回可协程的类型。 – 通过 `asio::awaitable` 可以与 Boost.Asio 或 libuv 等网络库无缝配合,实现高性能的异步 I/O。 – 在使用协程时需关注堆栈管理、对象移动和异常处理,以获得最佳性能和可维护性。 以上内容从基础概念到实际应用,帮助你快速上手 C++20 协程,打造更简洁、高效的异步代码。祝编码愉快!

发表评论