# 面试时没有回答好问题是很正常的,被面试官质疑时,要有今天不会明天会的底气,面后做好复盘,不在同一道题目上跌倒第二次,今天就从 C++中的协程概念及实现 背起来
C++中的协程概念及实现
# 简要回答
C++20引入了协程作为语言级别的异步编程支持。
协程是能够暂停执行并在之后恢复的函数,通过co_await、co_yield、co_return关键字实现。
它包含promise对象、coroutine handle和awaiter三个核心组件,提供比回调更简洁的异步代码编写方式。
# 详细回答
协程核心概念:
协程函数:包含co_await、co_yield、co_return的函数
Promise对象是指控制协程生命周期和返回值
Coroutine Handle被用于恢复或销毁协程的句柄
Awaiter通常定义co_await表达式的等待行为
协程的执行过程:
第一分配协程帧(存储局部变量和状态),第二创建promise对象,第三调用promise.get_return_object()获取返回给调用者的对象,第四执行协程体直到暂停或结束,第五通过handle恢复执行或销毁资源
# 代码示例
#include <iostream>
#include <coroutine>
#include <thread>
#include <chrono>
// 简单的生成器协程
struct Generator {
struct promise_type {
int current_value;
Generator get_return_object() {
return Generator{std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() { std::terminate(); }
std::suspend_always yield_value(int value) {
current_value = value;
return {};
}
void return_void() {}
};
std::coroutine_handle<promise_type> handle;
explicit Generator(std::coroutine_handle<promise_type> h) : handle(h) {}
~Generator() { if (handle) handle.destroy(); }
int value() { return handle.promise().current_value; }
bool move_next() {
if (!handle.done()) {
handle.resume();
return !handle.done();
}
return false;
}
};
Generator range(int start, int end) {
for (int i = start; i <= end; ++i) {
co_yield i; // 每次yield一个值并暂停
}
}
// 异步任务示例
struct AsyncTask {
struct promise_type {
int result;
AsyncTask get_return_object() {
return AsyncTask{std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() { std::terminate(); }
void return_value(int value) { result = value; }
};
std::coroutine_handle<promise_type> handle;
struct Awaiter {
bool await_ready() { return false; }
void await_suspend(std::coroutine_handle<> h) {
std::thread([h]() {
std::this_thread::sleep_for(std::chrono::seconds(1));
h.resume();
}).detach();
}
void await_resume() {}
};
Awaiter operator co_await() {
return Awaiter{};
}
};
AsyncTask async_work() {
std::cout << "开始异步工作..." << std::endl;
co_await AsyncTask::Awaiter{};
std::cout << "异步工作完成!" << std::endl;
co_return 42;
}
int main() {
// 生成器示例
auto gen = range(1, 5);
while (gen.move_next()) {
std::cout << "生成值: " << gen.value() << std::endl;
}
// 异步任务示例
auto task = async_work();
std::this_thread::sleep_for(std::chrono::seconds(2));
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# 知识拓展
- 协程vs线程
线程:是操作系统调度,真正的并行过程,并且上下文切换的成本比较高
协程:是用户态调度,同于协作式多任务,它的切换成本极低
- 协程vs回调
回调:容易产生回调地狱,错误处理复杂
协程:线性代码风格,异常处理自然
- 知识图解

- 适用场景
异步I/O操作:网络请求、文件读写
生成器:大数据集流式处理
状态机:复杂状态转换逻辑
游戏开发:角色行为、动画序列
GUI编程:保持UI响应的后台任务
- 面试官很能追问
Q1: 协程和线程的主要区别是什么?
A1: 线程是操作系统级别的并发单位,由内核调度,支持真正并行但上下文切换成本高。协程是用户态的轻量级线程,由程序自己调度,切换成本低但不支持真正并行。
Q2: 协程帧通常包含哪些内容?
A2: 协程帧包含:promise对象、函数参数、局部变量、恢复点信息、挂起时寄存器状态等。它本质上是一个保存协程执行状态的堆分配对象。
Q3: co_await表达式的三阶段流程?
A3:
await_ready:检查是否可以直接返回结果
await_suspend:挂起协程,安排恢复时机
await_resume:恢复时获取结果或继续执行
Q4: 如何避免协程的内存泄漏?
A4: 确保coroutine_handle正确销毁:使用RAII包装器、在final_suspend中适当处理、避免循环引用。推荐使用智能指针管理协程生命周期。
评论
验证登录状态...