**C++20 协程的基础与实际应用示例**

C++20 在标准库中首次正式引入协程(coroutine)概念,为异步编程和生成器提供了强大而简洁的语法。本文将从协程的基本构成、实现机制、典型用法以及常见坑点四个角度,带你快速掌握 C++20 协程,并给出实用示例。


1. 协程的基本概念

协程是一种轻量级的协作式多任务调度机制。它允许函数在执行过程中挂起(co_awaitco_yieldco_return)并在后续恢复,进而实现非阻塞的异步流、延迟执行和生成器等功能。

C++20 协程的核心语法包括:

关键词 作用 说明
co_await 挂起协程等待异步结果 只能出现在协程中
co_yield 在协程中产生一个值,控制权交还给调用者 通常用于实现生成器
co_return 结束协程并返回值 可在协程结束时使用
co_return; 结束协程但不返回值 return; 类似但在协程中

协程函数返回的类型必须满足 协程返回类型awaitable, generator, task 等),而不是普通的 voidint


2. 协程的实现机制

在 C++20 中,编译器会把协程函数拆分为三部分:

  1. 协程句柄(std::coroutine_handle:控制协程的生命周期,提供 resume()destroy() 等方法。
  2. 协程状态机:存储挂起点、局部变量等。
  3. 悬空指针:在协程结束后指向完成状态。

协程的挂起与恢复是由 awaitable 对象的 await_suspendawait_resume 等函数完成的。开发者往往不需要手动管理这些细节,除非要实现自定义 awaitable。


3. 常见协程类型

类型 用途 示例
`std::generator
| 生成器,支持co_yield|generator fib()`
`std::task
| 异步任务,支持co_await|task async_add(int a, int b)`
`std::future
(C++20 之后) | 兼容旧式异步 |future fetch_data()`

注意std::generatorstd::task 是在 `

`、“ 等头文件中声明的。标准库实现差异较大,建议使用编译器自带的实验性实现或第三方库(如 `cppcoro`)。

4. 实际示例

下面给出一个完整的异步网络请求示例(使用 cppcoro 库),演示如何使用协程实现非阻塞 I/O。

#include <cppcoro/task.hpp>
#include <cppcoro/io_context.hpp>
#include <cppcoro/socket.hpp>
#include <cppcoro/awaitable.hpp>
#include <iostream>

using namespace cppcoro;

// 简单的异步读取函数
task<std::string> async_read(io_context& io, socket& sock, std::size_t n) {
    std::vector <char> buffer(n);
    auto bytes = co_await sock.read_some(buffer.data(), n);
    std::string result(buffer.data(), bytes);
    co_return result;
}

// 主程序
int main() {
    io_context io;
    socket sock(io);

    // 假设已经连接到服务器
    sock.connect("example.com", 80);

    // 发送 GET 请求
    std::string req = "GET / HTTP/1.1\r\nHost: example.com\r\n\r\n";
    sock.write_some(req.data(), req.size());

    // 异步读取响应
    auto fut = async_read(io, sock, 1024);
    io.run(); // 运行事件循环,直到任务完成

    std::cout << "Response: " << fut.get() << std::endl;
    return 0;
}

说明:

  • async_read 使用 co_await 挂起直到读取完成。
  • io_context 负责事件循环,类似于 asioio_context
  • 通过 `task ` 的 `get()` 方法获取异步结果。

5. 常见坑点与调试技巧

  1. 协程返回类型错误
    协程函数若不返回 awaitable 类型,编译器会报错。记得在返回 taskgenerator 等时使用正确的头文件。

  2. 生命周期管理
    协程句柄若在外部保留,必须手动 destroy(),否则会造成内存泄漏。使用 std::shared_ptr<coroutine_handle<>> 可以简化管理。

  3. 异常传播
    协程内部抛出的异常会传播到 co_await 的调用点,使用 try-catch 捕获。若未捕获,协程会终止并抛出异常。

  4. 调试协程
    编译器通常会在 -fcoroutines 下生成调试符号;可使用 gdbbreak * 等手段查看协程状态。


6. 结语

C++20 的协程为现代 C++ 带来了强大的异步编程模型。通过合理组合 co_awaitco_yieldco_return,我们可以轻松实现事件驱动、生成器、协程管道等复杂逻辑。熟练掌握协程的语法与底层实现,将极大提升代码的可读性与性能。祝你在协程世界里玩得开心!


发表评论