0

0

C++如何实现协程?(C++20 coroutine入门)

冰火之心

冰火之心

发布时间:2026-03-03 13:57:23

|

520人浏览过

|

来源于php中文网

原创

c++20协程是编译器与运行时协作的状态机机制,非语法糖;co_await等关键字触发编译器生成挂起/恢复逻辑,但调度、内存管理、线程切换需手动实现。

c++如何实现协程?(c++20 coroutine入门)

协程不是语法糖,是编译器+运行时协作的机制

直接说结论:C++20 的 co_awaitco_yieldco_return 本身不提供调度器或线程切换,它们只是让编译器生成状态机代码。你写一个 co_await 表达式,编译器会把它拆成「挂起前保存现场」「恢复时跳回正确位置」两部分,但谁来保存栈、谁来唤醒、在哪执行——全得你自己管。

常见错误现象:std::coroutine_handle 拿到后直接 .resume() 却崩溃;或者协程函数返回后立刻访问局部变量,结果读到垃圾值——因为栈帧早被回收了。

  • 所有协程对象(比如 Task 类型)必须自己管理内存生命周期,不能依赖栈分配
  • co_await 右值必须实现 await_ready/await_suspend/await_resume 三个成员函数,缺一不可
  • 如果你没写自定义 promise_type,编译器会用默认空实现,但那样 co_return 后无法取返回值,也捕获不到异常

怎么写一个最小可运行的 Task 协程类型

目标很实际:让函数能用 co_await 等待另一个协程,并拿到它的返回值。关键在 promise_type 的设计。

使用场景:写异步 I/O 封装、测试协程调度逻辑、理解挂起/恢复链路。

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

struct Task {
  struct promise_type {
    Task get_return_object() { return {}; }
    std::suspend_never initial_suspend() { return {}; }
    std::suspend_always final_suspend() noexcept { return {}; }
    void unhandled_exception() { std::terminate(); }
    void return_value(int v) { value = v; }
    int value = 0;
  };
};

注意点:

Pebblely
Pebblely

AI产品图精美背景添加

下载
  • initial_suspend() 返回 std::suspend_never 表示协程创建后立即执行,不是懒启动
  • final_suspend() 必须返回 std::suspend_always,否则协程结束时 handle 自动销毁,你再拿它就 UB
  • 返回值存在 promise_type 成员里,不是靠栈传参;所以 Task 对象本身要持有 std::coroutine_handle<promise_type></promise_type> 才能读 value

co_await 之后到底发生了什么?别猜,看 suspend 调用时机

很多人以为 co_await expr 是“等 expr 完成”,其实它只做三件事:调用 expr.await_ready() → 若返回 false,调用 expr.await_suspend(handle) → 挂起当前协程。

容易踩的坑:

  • await_suspend 返回 true:表示你已接管调度,编译器不会再自动 resume;返回 false 或 void:编译器会在当前线程立刻 resume
  • 如果 await_suspendhandle 交给另一个线程去 resume(),那它必须确保该 handle 在 resume 前不被销毁(比如用 std::shared_ptr 包一层)
  • await_resume() 的返回值就是 co_await expr 整个表达式的值,类型必须严格匹配,否则编译失败

为什么调试协程时断点经常跳错位置?

因为编译器把协程函数重写成了巨型 switch-case 状态机,所有 co_await 都变成 goto 跳转。调试器看到的“当前行”往往是状态机的 dispatcher,不是你写的源码行。

实操建议:

  • 优先用 printf 或日志打在 await_suspend / await_resume 里,比依赖 IDE 断点更可靠
  • 不要对协程函数单步调试——切到汇编或反编译看状态编号更有效
  • Clang 编译加 -g -O1-O2 更利于调试;GCC 下 -Og 是专为调试协程优化的选项

最麻烦的地方在于:协程的“堆栈”是分散在堆上的多个 promise 对象 + 手动维护的状态字段,没有传统调用栈可回溯。一旦 suspend/resume 链路断掉,就只能靠日志和 handle 生命周期检查来定位。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
switch语句用法
switch语句用法

switch语句用法:1、Switch语句只能用于整数类型,枚举类型和String类型,不能用于浮点数类型和布尔类型;2、每个case语句后面必须跟着一个break语句,以防止执行其他case的代码块,没有break语句,将会继续执行下一个case的代码块;3、可以在一个case语句中匹配多个值,使用逗号分隔;4、Switch语句中的default代码块是可选的等等。

566

2023.09.21

Java switch的用法
Java switch的用法

Java中的switch语句用于根据不同的条件执行不同的代码块。想了解更多switch的相关内容,可以阅读本专题下面的文章。

439

2024.03.13

printf用法大全
printf用法大全

php中文网为大家提供printf用法大全,以及其他printf函数的相关文章、相关下载资源以及各种相关课程,供大家免费下载体验。

76

2023.06.20

fprintf和printf的区别
fprintf和printf的区别

fprintf和printf的区别在于输出的目标不同,printf输出到标准输出流,而fprintf输出到指定的文件流。根据需要选择合适的函数来进行输出操作。更多关于fprintf和printf的相关文章详情请看本专题下面的文章。php中文网欢迎大家前来学习。

300

2023.11.28

go语言goto的用法
go语言goto的用法

本专题整合了go语言goto的用法,阅读专题下面的文章了解更多详细内容。

138

2025.09.05

javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

186

2023.11.23

java中void的含义
java中void的含义

本专题整合了Java中void的相关内容,阅读专题下面的文章了解更多详细内容。

125

2025.11.27

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

432

2023.07.18

Swift iOS架构设计与MVVM模式实战
Swift iOS架构设计与MVVM模式实战

本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

3

2026.03.03

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 10.6万人学习

C 教程
C 教程

共75课时 | 5.1万人学习

C++教程
C++教程

共115课时 | 20.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号