C++怎么使用协程_C++20协程基础入门【异步】

1次阅读

c++20协程不自动并发,需配合调度器或awaitable封装才能实现异步;其核心是promise_type控制行为,co_await用于等待、co_yield仅用于generator;编译器支持与abi存在差异,调试和发布需注意细节。

C++怎么使用协程_C++20协程基础入门【异步】

协程不是线程co_await 不会自动并发

很多人一看到“协程”就默认能并行跑任务,结果发现 co_await 之后代码还是顺序执行,甚至比普通函数还慢。这是因为 C++20 协程本身不带调度器——它只提供挂起/恢复的机制,不负责把协程扔给线程池或事件循环

真正异步,得靠你选的 executor 或库(比如 std::executionlibunifexcppcoro),或者自己写一个简单的轮询器。标准库目前只提供底层协程支持(promise_typeco_awaitco_return),没给现成的 asyncTask 类型。

  • 别直接在主线程co_await 一个耗时 I/O 操作,除非你有配套的 awaitable 封装(比如基于 epollIOCP 的 awaiter)
  • 想模拟“异步”,最简方式是用 std::Thread + std::future 包一层,但这和协程无关,只是借个壳
  • 调试时注意:协程函数返回的是一个对象(如 Task<int></int>),不是值;不 co_await 它,它根本不会运行

promise_type 是协程行为的开关,不是可选配件

所有协程函数编译后都会隐式实例化一个 promise_type,它控制协程怎么初始化、挂起、异常处理、返回值怎么包装。如果你没显式定义,编译器会尝试找默认的(比如 std::suspend_always),但多数时候找不到,直接报错:

Error: no type named 'promise_type' in 'MyCoroutine'

立即学习C++免费学习笔记(深入)”;

常见场景是想写一个轻量 Generator<t></t>Task<t></t>,结果忘了在返回类型里嵌套定义 promise_type

  • 必须满足:静态成员 get_return_object()initial_suspend()final_suspend()return_value(T)(或 return_void()
  • initial_suspend() 返回 std::suspend_always 表示创建即挂起;返回 std::suspend_never 表示立即执行到第一个 co_await
  • 漏掉 unhandled_exception()?一旦协程里抛异常,程序直接 terminate

别拿 co_yieldco_await

co_yield 只用于 generator 类型(比如遍历生成器),它的语义是“产出一个值并挂起”,底层调用的是 promise.yield_value();而 co_await 是通用挂起点,依赖 await_ready/await_suspend/await_resume 三件套。

典型错误:在非 generator 协程里写 co_yield some_awaitable,编译不过,因为 promise_type 没实现 yield_value,而且语义也不对。

  • co_yield x → 等价于 promise.yield_value(x); + 挂起
  • co_await x → 触发 x.await_ready() 等三个函数,和 promise 关系间接
  • 想让协程“等一个 future”,必须用 co_await,不是 co_yield;generator 里用 co_yield 是为了迭代,不是为了等待

MSVC 和 Clang 对协程的支持细节差异真不小

Clang 15+ 默认开启协程支持(-std=c++20 -fcoroutines),而 MSVC 从 19.30 开始才稳定支持,且要求 windows SDK ≥ 10.0.20348.0;GCC 目前(13.x)仍实验性支持,需加 -fcoroutines 且禁用 PCH。

更麻烦的是 ABI 兼容问题:不同编译器生成的协程对象内存布局不一致,跨 DLL 边界传 Task<int></int> 很可能崩溃。

  • MSVC 下 co_await 某些自定义 awaitable 时,若 await_suspend 返回 void,可能静默失败;Clang 要求必须返回 boolcoroutine_handle
  • 调试时,MSVC 的 visual studio 调试器对协程帧支持有限,常显示 “coroutine frame not available
  • 发布构建务必关掉 /Zi(PDB 调试信息),否则 MSVC 可能因协程元数据生成巨大 PDB 文件

协程真正的门槛不在语法,而在你得同时理清:promise 控制流、awaiter 状态机、调度时机、以及编译器实际生成了什么对象。少一个环节,就容易卡在“协程没执行”“值没拿到”“程序卡死”这种无提示问题上。

text=ZqhQzanResources