**C++20 协程:在异步 IO 中优雅地使用**

协程是 C++20 引入的一项强大特性,它让异步编程变得像同步编程一样直观。下面我们通过一个简单的网络请求示例,演示如何使用协程实现异步 IO,并讨论常见的陷阱与最佳实践。


1. 协程基础

协程通过 co_yieldco_returnco_await 等关键字实现“暂停”和“恢复”功能。协程函数的返回类型必须是 std::experimental::generator、**`std::experimental::generator

`** 或 **`std::experimental::task`**(后者在标准 C++20 中是 `std::future` 的一个包装)。 “`cpp #include #include #include #include std::future async_http_get(const std::string& url); “` ### 2. 异步 HTTP GET 的协程实现 下面的示例演示如何用协程包装 `std::async`,并在等待期间让线程池或事件循环继续执行其它任务。 “`cpp #include #include #include #include #include #include // 模拟网络请求的耗时操作 std::string fake_http_get(const std::string& url) { std::this_thread::sleep_for(std::chrono::seconds(2)); return “Response from ” + url; } // 把耗时操作包装成协程 std::future async_http_get(const std::string& url) { // 这里用 std::async 来模拟异步操作,真实项目可替换成 ASIO 等库 return std::async(std::launch::async, fake_http_get, url); } // 协程入口 std::future process_requests(const std::vector& urls) { for (const auto& url : urls) { // 发起异步请求 std::future fut = async_http_get(url); // 这里可以做其他工作,例如更新 UI std::cout << "发起请求: " << url << std::endl; // 等待结果 std::string response = co_await fut; // C++20 协程语法 // 处理结果 std::cout << "收到响应: " << response < **注意**:C++20 标准库并没有直接提供 `co_await` 的实现,`std::future` 本身不支持协程。实际使用时应依赖 **`std::experimental::task `** 或第三方库(如 `cppcoro`、`libcoro`、`asio` 的协程适配器)。上例为伪代码,展示思路。 ### 3. 事件循环与协程 在实际项目中,协程往往与事件循环(Event Loop)配合使用。下面是一个简化的事件循环示例,展示如何将协程与 `select`/`epoll` 等 IO 复用机制整合。 “`cpp #include #include #include class EventLoop { public: EventLoop() { epfd_ = epoll_create1(0); } ~EventLoop() { close(epfd_); } void add_fd(int fd, int events) { epoll_event ev{}; ev.events = events; ev.data.fd = fd; epoll_ctl(epfd_, EPOLL_CTL_ADD, fd, &ev); } void run() { while (true) { std::array events; int nfds = epoll_wait(epfd_, events.data(), events.size(), -1); for (int i = 0; i < nfds; ++i) { // 根据事件类型唤醒相应的协程 handle_event(events[i].data.fd, events[i].events); } } } private: int epfd_; void handle_event(int fd, int events) { /* … */ } }; “` ### 4. 常见陷阱 | 陷阱 | 说明 | 对策 | |——|——|——| | **协程对象生命周期** | 协程返回值持有协程状态,若返回值被提前销毁会导致悬挂。 | 确保返回值存活,或使用 `std::shared_ptr` 包装。 | | **`co_await` 与 `std::future` 不兼容** | `std::future` 无 `await_ready` 等成员。 | 使用 `std::experimental::task` 或第三方库。 | | **错误传播** | 协程内部抛出的异常默认会通过返回的 `std::future` 抛出。 | 使用 `try/catch` 捕获,或通过 `std::expected` 等包装。 | | **性能开销** | 每个协程需要堆栈空间,过多协程会导致堆栈碎片。 | 采用协程池或轻量协程实现。 | ### 5. 小结 C++20 协程为异步 IO 提供了直观、可组合的语法糖。通过协程与事件循环、任务调度器的配合,可以写出既易读又高性能的异步代码。虽然标准库尚未完全提供协程友好的异步组件,但现有的第三方库已足以满足大多数需求。未来的 C++ 可能会在标准库中加入更完善的协程支持,让异步编程更进一步。

发表评论