文章目录
1. 协程(Coroutine)简介
协程是一种特殊的函数,可以在执行过程中暂停并在稍后恢复执行。与普通函数不同,协程可以在任意点挂起(yield)并将控制权返回给调用者,同时保留其状态,以便后续恢复执行。协程常用于异步编程、生成器、状态机等场景。
C++20 引入了对协程的原生支持,使得开发者可以更方便地编写协程代码。
2. 协程的核心概念
-
挂起(Suspend):
-
协程可以在执行过程中挂起,将控制权返回给调用者。
-
挂起时,协程的状态(局部变量、执行位置等)会被保存。
-
-
恢复(Resume):
- 协程可以从挂起的位置恢复执行,继续运行。
-
协程帧(Coroutine Frame):
-
协程的状态(局部变量、挂起点等)存储在协程帧中。
-
协程帧在堆上分配,生命周期由协程库管理。
-
-
协程句柄(Coroutine Handle):
- 用于控制协程的执行(挂起、恢复、销毁等)。
3. C++20 协程的关键组件
-
co_await:
-
用于挂起协程,等待某个操作完成。
-
操作可以是异步 I/O、定时器、任务等。
-
-
co_yield:
-
用于生成值并挂起协程。
-
常用于实现生成器(Generator)。
-
-
co_return:
- 用于从协程返回值并结束协程。
-
协程类型:
- 协程函数必须返回一个符合协程接口的类型(如 std::future、generator 等)。
4. 协程示例
4.1 示例 1:简单的协程(生成器)
以下示例实现了一个生成器,用于生成一系列整数。
#include <iostream>
#include <coroutine>
// 定义一个生成器类型
template<typename T>
struct Generator {
struct promise_type {
T 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(T val) { value = val; 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(); } // 销毁协程
T next() {
handle.resume(); // 恢复协程
return handle.promise().value; // 返回生成的值
}
};
// 定义一个生成器协程
Generator<int> range(int start, int end) {
for (int i = start; i <= end; ++i) {
co_yield i; // 生成值并挂起
}
}
int main() {
auto gen = range(1, 5); // 创建生成器
for (int i = 0; i < 5; ++i) {
std::cout << gen.next() << std::endl; // 获取生成的值
}
return 0;
}
输出:
1
2
3
4
5
4.2 示例 2:异步任务
以下示例实现了一个简单的异步任务,模拟异步操作。
#include <iostream>
#include <coroutine>
#include <thread>
#include <chrono>
// 定义一个异步任务类型
struct AsyncTask {
struct promise_type {
int value; // 任务的结果
AsyncTask get_return_object() { return AsyncTask{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(); } // 异常处理
void return_value(int val) { value = val; } // 返回值
};
std::coroutine_handle<promise_type> handle;
explicit AsyncTask(std::coroutine_handle<promise_type> h) : handle(h) {}
~AsyncTask() { if (handle) handle.destroy(); } // 销毁协程
int get() {
handle.resume(); // 恢复协程
return handle.promise().value; // 返回结果
}
};
// 定义一个异步任务协程
AsyncTask computeAsync() {
std::cout << "Starting async computation..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2)); // 模拟耗时操作
co_return 42; // 返回结果
}
int main() {
auto task = computeAsync(); // 创建异步任务
std::cout << "Waiting for result..." << std::endl;
int result = task.get(); // 获取结果
std::cout << "Result: " << result << std::endl;
return 0;
}
输出:
Starting async computation...
Waiting for result...
Result: 42
5. 协程的优势
-
简化异步编程:
- 协程可以将异步代码写成同步风格,避免回调地狱。
-
高效的状态管理:
- 协程的状态由编译器自动管理,无需手动维护状态机。
-
灵活的控制流:
- 协程可以挂起和恢复,适用于生成器、流处理等场景。
6. 总结
-
协程是一种可以挂起和恢复的函数,适用于异步编程、生成器等场景。
-
C++20 引入了原生协程支持,通过 co_await、co_yield 和 co_return 实现协程逻辑。
-
协程的核心是协程帧和协程句柄,用于保存状态和控制执行。
-
通过生成器和异步任务的示例,可以更好地理解协程的用法和优势。