协程是 C++ 23 里最令人期待的特性之一,它为异步编程带来了更简洁、更高效的解决方案。下面让我们一步步拆解协程的概念、实现方式以及如何在实际项目中使用它们。
-
协程到底是什么?
协程是一种轻量级的函数,能够在执行过程中暂停(yield)并在以后恢复执行。与传统线程相比,协程的上下文切换成本更低,且更易于维护。协程的核心是co_await、co_yield和co_return这三个关键字。 -
协程的基本语法
#include <iostream> #include <coroutine> #include <string_view>
struct Generator { struct promise_type; using handle_type = std::coroutine_handle
; struct promise_type { std::string_view current_value; Generator get_return_object() { return Generator{handle_type::from_promise(*this)}; } std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } void unhandled_exception() { std::terminate(); } std::suspend_always yield_value(std::string_view v) { current_value = v; return {}; } void return_void() {} }; handle_type coro; explicit Generator(handle_type h) : coro(h) {} ~Generator() { coro.destroy(); } struct iterator { handle_type coro; bool operator==(std::default_sentinel_t) const noexcept { return !coro || coro.done(); } bool operator!=(std::default_sentinel_t) const noexcept { return !operator==(std::default_sentinel_t{}); } iterator(handle_type h) : coro(h) {} iterator& operator++() { coro.resume(); return *this; } std::string_view operator*() const { return coro.promise().current_value; } }; iterator begin() { coro.resume(); return iterator{coro}; } std::default_sentinel_t end() { return {}; } }; “` 使用上述 `Generator`,我们可以轻松生成一个字符串序列。 3. **协程的执行模型** – `initial_suspend`:协程创建后是否立即暂停。 – `yield_value`:协程产生一个值,控制权返回给调用者。 – `final_suspend`:协程结束时的暂停点。 4. **协程的典型应用场景** – **异步 I/O**:在网络编程中,用协程替代回调或状态机,代码更易读。 – **生成器**:如上例所示,惰性生成数据流。 – **并发流水线**:将多个协程串联,实现高效的数据流处理。 5. **协程与 std::future 的区别** `std::future` 在 C++ 20 引入,用于等待异步任务完成。协程则提供更细粒度的控制:你可以在任意点 `co_await`、`co_yield`,并且不需要显式地分配线程。 6. **实战案例:简易 HTTP 客户端** “`cpp #include #include #include #include #include // 需要 ASIO 库 using asio::awaitable; using asio::use_awaitable; using asio::ip::tcp; awaitable http_get(std::string host, std::string path) { auto executor = co_await asio::this_coro::executor; tcp::resolver resolver(executor); auto endpoints = co_await resolver.async_resolve(host, “80”, use_awaitable); tcp::socket socket(executor); co_await asio::async_connect(socket, endpoints, use_awaitable); std::string request = “GET ” + path + ” HTTP/1.1\r\n” + “Host: ” + host + “\r\n” + “Connection: close\r\n\r\n”; co_await asio::async_write(socket, asio::buffer(request), use_awaitable); std::vector buffer(1024); std::string response; for (;;) { std::size_t n = co_await socket.async_read_some(asio::buffer(buffer), use_awaitable); if (n == 0) break; response.append(buffer.data(), n); } co_return response; } int main() { asio::io_context io; asio::co_spawn(io, []() -> awaitable { std::string body = co_await http_get(“example.com”, “/”); std::cout << "Response: " << body.substr(0, 200) << "…\n"; }, asio::detached); io.run(); } “` 此代码展示了如何在协程里使用 ASIO 的异步 I/O,简化了回调地狱。 7. **性能与调试** – 协程生成的代码实际上是一种状态机,编译器会把 `co_await`、`co_yield` 转化为 `resume`、`suspend`。 – 调试时可使用 `std::experimental::coroutine_traits` 观察状态。 8. **总结** 协程为 C++ 开发者提供了更自然的异步编程模型,减少了繁琐的状态机代码。掌握协程的核心概念(`co_await`、`co_yield`、`co_return`)以及如何在实际项目中组合使用,将极大提升代码可读性与性能。继续关注 C++ 23 的其他新特性,如 `std::atomic_ref`、`std::format` 等,共同打造更现代、更高效的 C++ 程序。