C++中的协程:如何使用C++20标准实现异步编程

C++20 引入了协程(coroutine)概念,使得在单线程中实现异步操作变得更加自然和高效。相比传统的回调或线程池,协程能够在需要等待 I/O、网络请求等耗时操作时挂起执行,随后恢复,整个流程几乎不需要显式的状态机管理。本文将从协程的基本概念、实现细节以及实际应用场景展开,帮助你快速掌握 C++20 协程的使用方法。

1. 协程基本语法

协程函数通过关键字 co_awaitco_yieldco_return 来定义。与普通函数不同,协程函数的返回类型不是 T,而是一个 promise type,通常是 `std::generator

`、`std::task` 或自定义类型。 “`cpp #include #include struct hello { struct promise_type { auto get_return_object() { return hello{}; } std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } void unhandled_exception() { std::terminate(); } void return_void() {} }; }; hello world() { std::cout #include template struct task { struct promise_type { std::promise promise; task get_return_object() { return task{promise.get_future()}; } std::suspend_never initial_suspend() { return {}; } std::suspend_never final_suspend() noexcept { return {}; } void unhandled_exception() { promise.set_exception(std::current_exception()); } void return_value(T value) { promise.set_value(value); } }; std::future fut; explicit task(std::future f) : fut(std::move(f)) {} operator std::future &() { return fut; } }; task async_add(int a, int b) { co_return a + b; } “` 使用方式: “`cpp auto t = async_add(3, 4); int result = t.fut.get(); // 结果为 7 “` ### 2.2 生成器(Generator) C++20 标准提供 `std::generator`(在实验版 ` ` 头文件中)。以下示例生成斐波那契数列: “`cpp #include #include std::generator fib(int n) { int a = 0, b = 1; for (int i = 0; i ); // 挂起并注册恢复 T await_resume(); // 获得结果 }; “` 例如,使用 `std::future`: “`cpp struct future_awaiter { std::future fut; bool await_ready() const noexcept { return fut.wait_for(std::chrono::seconds(0)) == std::future_status::ready; } void await_suspend(std::coroutine_handle h) { std::thread([f = std::move(fut), h]() mutable { f.wait(); h.resume(); }).detach(); } int await_resume() { return fut.get(); } }; future_awaiter make_awaiter(std::future f) { return {std::move(f)}; } “` ## 4. 常见使用场景 1. **网络 I/O**:配合异步 I/O 库(如 Boost.Asio 的 `async_read`)实现非阻塞服务器。 2. **文件读写**:与异步文件系统接口结合,在文件读取完成前挂起。 3. **协作式多任务**:在单线程游戏循环中通过协程实现脚本式 AI 或动画控制。 4. **生成器**:遍历大数据集时按需产生元素,节省内存。 ## 5. 性能注意 – 协程切换是轻量级的,但不如线程切换原生;仍需注意过度挂起导致的上下文切换。 – `std::suspend_never` 可以减少不必要的挂起,提升性能。 – 对于频繁小任务,建议将任务合并为单一协程以降低协程管理成本。 ## 6. 代码示例:异步下载文件 “`cpp #include #include #include struct async_download { asio::ip::tcp::socket sock; std::string data; async_download(asio::io_context& io) : sock(io) {} struct promise_type { async_download* self; auto get_return_object() { return self; } std::suspend_always initial_suspend() { return {}; } std::suspend_always final_suspend() noexcept { return {}; } void unhandled_exception() { std::terminate(); } void return_void() {} }; auto operator co_await() const { struct awaiter { async_download& self; bool await_ready() const noexcept { return false; } void await_suspend(std::coroutine_handle h) { asio::async_read(self.sock, asio::buffer(self.data), [h](std::error_code ec, std::size_t) mutable { h.resume(); }); } std::string await_resume() { return self.data; } }; return awaiter{*const_cast(this)}; } }; async_download fetch(asio::io_context& io, std::string host, std::string port) { using namespace asio; tcp::resolver resolver(io); auto endpoints = resolver.resolve(host, port); async_download d(io); co_await asio::async_connect(d.sock, endpoints, use_awaitable); co_return d; } int main() { asio::io_context io; auto task = fetch(io, “example.com”, “80”); io.run(); std::cout

发表评论