cpp-expert/coroutine-mechanism:协程机制演示
本目录收录 languages/cpp/expert/coroutine-mechanism.md 的完整可运行代码与实测数据。
要回答的问题:协程到底怎么实现"暂停/恢复"?编译器把函数体改成了什么?
| 文件 | 说明 |
|---|---|
manual.cpp | 手工状态机版协程:用 struct + enum + switch 复刻 C++20 协程的编译器产物,任意编译器可编译运行 |
cxx20.cpp | C++20 标准协程参考实现(需 GCC 10+ / Clang 14+,-std=c++20 -fcoroutines),与手工版语义等价 |
Makefile | make run 运行手工版;make asm 看 -O2 下状态机被拍平成循环 |
run-remote.sh | 服务器一键实测脚本 |
probe.sh | 反汇编探测脚本(-O0 resume 循环 / -O2 拍平) |
编译与运行
bash
make # 构建手工版(-O0 -g -std=c++11,无需协程支持)
make run # 运行:yield 1 / yield 2 / yield 3 / done
make asm # 观察 -O2 下状态机被编译器拍平成普通循环
make cxx20 # 尝试编译 C++20 参考实现(旧编译器会提示不支持)实测输出(Linux, g++ 4.8.5)
运行输出
yield 1
yield 2
yield 3
done手工状态机版与 cxx20.cpp(新编译器上)输出完全一致。
反汇编证据
-O0:main 是一个真实的协程调度循环——反复调用 IntGen::resume()、IntGen::done()、IntGen::value():
4005c3: callq 40061c <IntGen::resume()>
4005c8: ...
4005e2: callq 40068e <IntGen::value() const>
4005f3: callq 400470 <printf@plt>
400609: jne 4005bc <main+0x1f>-O2:整个状态机被编译器识别并拍平成一条普通循环,resume 函数消失:
4004b3: lea 0x1(%rbx),%esi # 算出下一个值
4004c0: callq 400470 <printf@plt>
4004c5: cmp $0x2,%ebx # 到 3 了吗?
4004c8: jle 4004b3 # 没到就回去这正是 C++20 协程 HALO 类优化(堆分配消除 + 状态机折叠)的同款原理。
C++20 参考实现
cxx20.cpp 在 GCC 10+ 上 -std=c++20 -fcoroutines 编译运行(本站演示服务器 GCC 4.8/8 不支持,属预期)。源码本身可直接对照 manual.cpp 阅读:co_yield 对应的就是手工版的"保存状态标签 + return",resume() 对应 h_.resume()。