文章目录
- [深入理解 C++20 协程核心机制](#深入理解 C++20 协程核心机制)
-
- 一、核心概念拆解(由浅入深)
-
- [1. `std::coroutine_handle` - 协程的"遥控器"](#1.
std::coroutine_handle- 协程的"遥控器") - [2. `noop_coroutine` 系列 - 空操作协程](#2.
noop_coroutine系列 - 空操作协程) - [3. `await_transform()` - 等待转换器](#3.
await_transform()- 等待转换器) - [4. `await_suspend()` - 等待挂起逻辑](#4.
await_suspend()- 等待挂起逻辑)
- [1. `std::coroutine_handle` - 协程的"遥控器"](#1.
- 二、完整代码示例(修正版)
- 三、关键场景深入解析
- 四、常见错误与修正
-
- 错误1:混淆协程句柄类型
- 错误2:永远挂起的协程
- [错误3:在 `await_transform` 中忘记转发](#错误3:在
await_transform中忘记转发)
- 五、总结
深入理解 C++20 协程核心机制
一、核心概念拆解(由浅入深)
C++20 协程的底层核心组件包括 noop_coroutine 系列工具、协程句柄 std::coroutine_handle,以及控制协程挂起恢复的关键逻辑 await_transform 和 await_suspend。
1. std::coroutine_handle - 协程的"遥控器"
std::coroutine_handle<Promise> 是协程的句柄,相当于协程的"遥控器"。通过它可以:
- 恢复 (resume) 协程执行
- 销毁 (destroy) 协程状态
- 检查 (done) 协程是否完成
- 访问 (promise) 协程的承诺对象
两种形式:
cpp
// 1. 通用协程句柄(类型擦除)
std::coroutine_handle<> h; // 等价于 std::coroutine_handle<void>
// 2. 具体承诺类型句柄
struct MyPromise { /* ... */ };
std::coroutine_handle<MyPromise> specific_h;
重要澄清:
std::coroutine_handle<>是std::coroutine_handle<void>的别名std::coroutine_handle<void>和std::coroutine_handle<noop_coroutine_promise>是不同的类型std::coroutine_handle<>不能直接操作noop_coroutine_promise类型的协程
2. noop_coroutine 系列 - 空操作协程
"noop" = "no operation"(无操作)。这是一组特殊工具,用于创建无实际效果的协程句柄。
组件说明:
cpp
// 1. 空操作协程的承诺类型
struct noop_coroutine_promise;
// 2. 空操作协程句柄的类型别名
using noop_coroutine_handle =
std::coroutine_handle<noop_coroutine_promise>;
// 3. 工厂函数
noop_coroutine_handle noop_coroutine() noexcept;
关键特性:
resume()和destroy()是空操作(无效果)done()总是返回true- 所有
noop_coroutine()返回的句柄都相等 - 主要用于安全的占位符或默认值
为什么需要?
cpp
// 场景:异步操作类,需要一个安全的协程句柄作为成员默认值
class AsyncOperation {
std::coroutine_handle<> continuation_ = std::noop_coroutine();
// ^ 使用 noop 协程作为安全的默认值
public:
void complete() {
if (continuation_ != std::noop_coroutine()) {
continuation_.resume(); // 只恢复真实的协程
}
}
};
3. await_transform() - 等待转换器
await_transform 是 Promise 类型的可选成员函数,用于拦截和转换 co_await 表达式。
工作流程:
cpp
co_await expression;
// 编译器会转换为:
co_await promise.await_transform(expression); // 如果有定义
// 否则:
co_await expression; // 直接使用原表达式
4. await_suspend() - 等待挂起逻辑
await_suspend 是 Awaitable 类型的核心成员,控制协程挂起后的行为。
三种返回值类型:
cpp
// 1. 返回 void - 挂起后不自动恢复
void await_suspend(std::coroutine_handle<> h) {
// 当前协程已挂起,句柄 h 可供稍后恢复
schedule_for_later(h); // 可以存储 h 供以后使用
}
// 2. 返回 bool - 控制是否挂起
bool await_suspend(std::coroutine_handle<> h) {
if (should_suspend) {
return true; // true = 挂起协程
} else {
return false; // false = 不挂起,立即恢复
}
}
// 3. 返回 coroutine_handle<> - 挂起并恢复其他协程
std::coroutine_handle<> await_suspend(std::coroutine_handle<> h) {
// 返回要恢复的其他协程句柄
return other_coroutine_handle;
// ^ 当前协程挂起,自动恢复 other_coroutine_handle
}
二、完整代码示例(修正版)
原代码存在关键问题:await_suspend 返回 noop_coroutine() 会导致当前协程永远挂起。以下是修正后的完整示例:
cpp
#include <coroutine>
#include <iostream>
#include <string>
#include <functional>
// ========== 示例1:基本用法 ==========
struct BasicPromise {
struct promise_type {
BasicPromise get_return_object() { return {}; }
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void unhandled_exception() { std::terminate(); }
void return_void() {}
};
};
// 示例协程
BasicPromise basic_coroutine() {
std::cout << "Basic coroutine executing\n";
co_return;
}
// ========== 示例2:使用 await_transform ==========
struct LoggerPromise {
struct promise_type {
LoggerPromise get_return_object() { return {}; }
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void unhandled_exception() { std::terminate(); }
void return_void() {}
// 拦截所有 co_await,添加日志
template<typename T>
auto await_transform(T&& value) {
struct LoggingAwaitable {
T value;
bool await_ready() const noexcept {
std::cout << "[LOG] Checking if ready\n";
return false;
}
void await_suspend(std::coroutine_handle<> h) {
std::cout << "[LOG] Suspending coroutine\n";
// 立即恢复,演示目的
std::cout << "[LOG] Immediately resuming\n";
h.resume();
}
T await_resume() const {
std::cout << "[LOG] Resuming coroutine\n";
return value;
}
};
return LoggingAwaitable{std::forward<T>(value)};
}
};
};
LoggerPromise logging_coroutine() {
std::cout << "Before co_await\n";
int result = co_await 42;
std::cout << "After co_await, got: " << result << "\n";
co_return;
}
// ========== 示例3:await_suspend 不同返回值 ==========
struct SuspensionDemo {
struct promise_type {
SuspensionDemo get_return_object() { return {}; }
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void unhandled_exception() { std::terminate(); }
void return_void() {}
};
// 演示不同返回值的 Awaitable
struct BoolAwaitable {
bool should_suspend;
bool await_ready() const noexcept {
std::cout << "BoolAwaitable::await_ready\n";
return false;
}
// 返回 bool 版本
bool await_suspend(std::coroutine_handle<> h) {
std::cout << "BoolAwaitable::await_suspend\n";
if (should_suspend) {
// 存储句柄供以后恢复
std::cout << "Suspending, will resume later\n";
// 实际应用中,这里可能会将 h 加入任务队列
stored_handle = h;
return true; // 挂起
} else {
std::cout << "Not suspending\n";
return false; // 不挂起
}
}
void await_resume() {
std::cout << "BoolAwaitable::await_resume\n";
}
static inline std::coroutine_handle<> stored_handle;
};
struct HandleAwaitable {
std::coroutine_handle<> next_handle;
bool await_ready() const noexcept {
std::cout << "HandleAwaitable::await_ready\n";
return false;
}
// 返回 coroutine_handle 版本
std::coroutine_handle<> await_suspend(std::coroutine_handle<> h) {
std::cout << "HandleAwaitable::await_suspend\n";
std::cout << "Suspending current coroutine\n";
if (next_handle) {
std::cout << "Will resume another coroutine\n";
return next_handle; // 挂起当前,恢复下一个
} else {
std::cout << "No coroutine to resume\n";
return std::noop_coroutine(); // 只挂起,不恢复任何协程
}
}
void await_resume() {
std::cout << "HandleAwaitable::await_resume\n";
}
};
};
// ========== 示例4:noop_coroutine 的正确使用 ==========
void demonstrate_noop_coroutine() {
std::cout << "\n=== Demonstrating noop_coroutine ===\n";
// 获取 noop 协程句柄
auto noop_h = std::noop_coroutine();
std::cout << "noop_coroutine_handle obtained\n";
// 特性演示
std::cout << "noop_h.done() = " << noop_h.done() << "\n"; // true
// 这些调用都无实际效果
noop_h.resume(); // 无效果
noop_h.destroy(); // 无效果
// 比较两个 noop 句柄
auto noop_h2 = std::noop_coroutine();
std::cout << "noop_h == noop_h2: " << (noop_h == noop_h2) << "\n"; // true
// 不能转换为普通协程句柄
// std::coroutine_handle<void> void_h = noop_h; // 错误!类型不同
std::coroutine_handle<> void_h = std::coroutine_handle<>::from_address(noop_h.address());
std::cout << "Converted to void handle\n";
}
// ========== 示例5:综合应用 ==========
class TaskScheduler {
private:
std::vector<std::coroutine_handle<>> pending_tasks;
public:
// 调度协程执行
void schedule(std::coroutine_handle<> task) {
pending_tasks.push_back(task);
}
// 运行所有任务
void run_all() {
while (!pending_tasks.empty()) {
auto task = pending_tasks.back();
pending_tasks.pop_back();
if (task && task != std::noop_coroutine()) {
std::cout << "Running task...\n";
task.resume();
}
}
}
};
struct SchedulerAwarePromise {
static inline TaskScheduler* scheduler = nullptr;
struct promise_type {
SchedulerAwarePromise get_return_object() { return {}; }
std::suspend_never initial_suspend() { return {}; }
std::suspend_never final_suspend() noexcept { return {}; }
void unhandled_exception() { std::terminate(); }
void return_void() {}
auto await_transform(std::string msg) {
struct SchedulerAwaitable {
std::string msg;
TaskScheduler* scheduler;
bool await_ready() const noexcept { return false; }
// 使用 void 返回值,手动调度
void await_suspend(std::coroutine_handle<> h) {
std::cout << "Scheduling: " << msg << "\n";
if (scheduler) {
scheduler->schedule(h); // 将协程加入调度器
} else {
h.resume(); // 没有调度器,立即恢复
}
}
std::string await_resume() const {
return "Completed: " + msg;
}
};
return SchedulerAwaitable{std::move(msg), SchedulerAwarePromise::scheduler};
}
};
};
SchedulerAwarePromise scheduled_coroutine(std::string name) {
std::cout << "Coroutine " << name << " started\n";
auto result = co_await name;
std::cout << "Coroutine " << name << " got: " << result << "\n";
co_return;
}
// ========== 主函数 ==========
int main() {
std::cout << "=== Example 1: Basic Coroutine ===\n";
basic_coroutine();
std::cout << "\n=== Example 2: Logging with await_transform ===\n";
logging_coroutine();
std::cout << "\n=== Example 3: Different await_suspend returns ===\n";
// 演示 noop_coroutine
demonstrate_noop_coroutine();
std::cout << "\n=== Example 5: Task Scheduler ===\n";
TaskScheduler scheduler;
SchedulerAwarePromise::scheduler = &scheduler;
// 创建并调度协程
scheduled_coroutine("Task1");
scheduled_coroutine("Task2");
// 运行所有调度的任务
std::cout << "\nRunning scheduled tasks:\n";
scheduler.run_all();
std::cout << "\n=== All examples completed ===\n";
return 0;
}
三、关键场景深入解析
场景1:await_suspend 返回 noop_coroutine() 的正确理解
cpp
std::coroutine_handle<> await_suspend(std::coroutine_handle<> h) {
// 正确用法:当你想挂起当前协程,但不需要立即恢复任何其他协程时
return std::noop_coroutine();
// 这意味着:
// 1. 当前协程被挂起
// 2. 没有自动恢复其他协程
// 3. 当前协程的句柄 h 仍然有效,可以在其他地方恢复
}
警告: 如果返回 noop_coroutine() 后没有人恢复原来的协程句柄 h,协程会永远挂起!
场景2:实现协程链
cpp
struct CoroutineChain {
std::coroutine_handle<> next;
bool await_ready() const { return false; }
std::coroutine_handle<> await_suspend(std::coroutine_handle<> h) {
if (next) {
return next; // 挂起当前,恢复下一个
}
return std::noop_coroutine(); // 没有下一个,只挂起
}
void await_resume() {}
};
// 使用
co_await CoroutineChain{next_coroutine_handle};
场景3:条件挂起
cpp
struct ConditionalSuspend {
bool condition;
bool await_ready() const {
return condition; // 如果条件为真,就不挂起
}
bool await_suspend(std::coroutine_handle<> h) {
// 只有 await_ready 返回 false 时才调用
return true; // 挂起
}
void await_resume() {}
};
// 使用
co_await ConditionalSuspend{should_suspend};
四、常见错误与修正
错误1:混淆协程句柄类型
cpp
// 错误!
std::coroutine_handle<> h = std::noop_coroutine();
// 类型不匹配:左边是 coroutine_handle<void>,右边是 coroutine_handle<noop_coroutine_promise>
// 修正:
auto h = std::noop_coroutine(); // 正确,使用 auto
// 或:
std::coroutine_handle<> void_h =
std::coroutine_handle<>::from_address(std::noop_coroutine().address());
错误2:永远挂起的协程
cpp
// 错误:返回 noop_coroutine() 但没有人恢复原协程
std::coroutine_handle<> await_suspend(std::coroutine_handle<> h) {
return std::noop_coroutine(); // 协程永远挂起!
}
// 修正方案1:存储句柄供以后恢复
void await_suspend(std::coroutine_handle<> h) {
global_task_queue.push(h); // 加入任务队列
}
// 修正方案2:立即恢复
bool await_suspend(std::coroutine_handle<> h) {
return false; // 不挂起,立即恢复
}
错误3:在 await_transform 中忘记转发
cpp
// 错误:丢失了原表达式的类型信息
template<typename T>
auto await_transform(T&& value) {
MyAwaitable awaiter;
return awaiter; // 丢失了 value!
}
// 修正:正确转发
template<typename T>
auto await_transform(T&& value) {
return MyAwaitable{std::forward<T>(value)};
}
五、总结
-
std::coroutine_handle是协程操作的核心,有通用 (<>) 和具体 (<Promise>) 两种形式。 -
noop_coroutine系列 提供安全的空操作协程句柄,用于占位符或默认值。 -
await_transform允许 Promise 拦截和转换co_await表达式,实现统一处理逻辑。 -
await_suspend的三种返回值:void:挂起,需要手动恢复bool:控制是否挂起coroutine_handle<>:挂起并恢复指定协程
-
关键实践:
- 使用
noop_coroutine()作为安全的协程句柄默认值 - 确保挂起的协程最终会被恢复(避免内存泄漏)
- 理解不同类型协程句柄之间的转换规则
- 使用
掌握这些核心概念,你就能灵活控制 C++20 协程的挂起、恢复和转换逻辑,为构建异步框架、协程调度器等高级应用打下坚实基础。