cpp
1.协程是一种可以挂起和恢复的函数,适用于异步编程、生成器等场景。
2.一个函数如果包含以下关键字之一,即为协程函数:
2.1 co_await:用于暂停协程的执行,直到一个等待的操作完成
2.1.1 用法auto ret = co_await async_fun();//async_fun为awaitable等待对象
2.1.2 awaitable对象,它需要有3个成员函数:
bool await_ready():检查操作是否完成(为false,表示需要暂停)
{下面3个中的一个:
void await_suspend(std::coroutine_handle<> handle):处理协程挂起逻辑,启动异步逻辑,通常会保存协程句柄以便后续恢复
bool await_suspend(std::coroutine_handle<> handle):返回bool类型的
std::coroutine_handle<> handle await_suspend(std::coroutine_handle<> handle):返回handle类型的
}
T await_resume():恢复时调用,返回等待结果(T为结果类型)
2.1.3 默认的两个awaitable对象std::suspend_always、std::suspend_never
2.2 co_yield:用于生成一个值,然后暂停协程
语法:co_yield value; // 等价于 co_await promise.yield_value(value)
2.3 co_return:返回结果并结束协程
co_return expression; //用于返回最终结果并终止协程,结果会通过promise_type传递给调用者
3.协程函数必须返回一个符合协程接口的类型
3.1 这个类型通常是一个包含promise_type嵌套结构体的类或结构体
3.2 这个类型必须是可构造的,以便在get_return_object函数中创建实例
3.3 这个类型必须是可析构的,以确保在协程结束时能够正确清理资源
3.4 这个类型必须提供方法来控制协程的执行,如提供promise_type成员变量handle
4.promise_type结构体的要求(负责协程从创建到结束的整个生命周期管理)
4.1 必须包含一个get_return_object函数,该函数返回一个代表协程的对象
4.2 必须包含一个initial_suspend函数,该函数返回一个可等待对象,用于决定协程在创建后是否立即挂起
4.3 必须包含一个final_suspend函数,该函数返回一个可等待对象,用于决定协程在执行完co_return后是否挂起
4.4 必须包含一个return_void(协程函数返回void)或return_value(协程函数有返回值非void)函数
4.5 必须包含一个unhandled_exception函数,当协程内部抛出未捕获的异常时,该函数会被调用,用于处理异常
4.6 yield_value函数,配合co_yield语句,使协程能够暂停执行并向调用者返回值,同时保持协程的状态以便后续恢复执行。
每次co_yield都会触发yield_value函数的调用,从而实现一系列值的生成和传递
for:
struct TASK { //定义一个协程接口的类型
struct promise_type { //包含promise_type嵌套结构体
TASK get_return_object() { ... }
std::suspend_always initial_suspend() { ... }
std::suspend_always final_suspend() { ... }
void unhandled_exception() { ... }
std::suspend_always yield_value(T val) { ... }
void return_void() {}
};
std::coroutine_handle<promise_type> handle;//用于外部控制协程(恢复、销毁)的轻量级指针
TASK(...){...}
~TASK() {...}
};
TASK coroutineFun() { //协程函数
std::this_thread::sleep_for(std::chrono::seconds(200));
co_return ;
}
co_await流程测试:
cpp
static void _sleep()
{
std::cout << "sleep 200" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(200)); //
}
struct Task {
struct promise_type;
using handle_type = std::coroutine_handle<promise_type>;
// 承诺对象:协程内部状态管理者
struct promise_type {
//这个函数负责返回一个代表协程的对象
//这个返回的对象包含了协程的句柄等信息,用于外部对协程进行控制,比如恢复协程执行、检查协程状态等。它是协程与外部世界交互的重要桥梁
Task get_return_object() {
std::cout << "get_return_object" << std::endl;
_sleep();
return Task(handle_type::from_promise(*this));
}
//首次执行前是否立即挂起,它返回一个类型为std::suspend_always或std::suspend_never的对象
//如果返回std::suspend_always,协程在创建后会立即挂起,直到被显式恢复;
//如果返回std::suspend_never,协程会立即开始执行其函数体
std::suspend_never initial_suspend() {
std::cout << "initial_suspend" << std::endl;
_sleep();
return {};
}
//该函数决定协程在执行完co_return后,是否挂起。同样返回std::suspend_always或std::suspend_never类型的对象
//返回std::suspend_always意味着协程在co_return后会挂起,此时可以进行一些清理工作(如果需要),然后再由外部决定是否销毁协程;
//返回std::suspend_never则表示协程执行完co_return后直接结束,其资源会被立即清理
std::suspend_always final_suspend() noexcept {
std::cout << "final_suspend" << std::endl;
_sleep();
return {};
}
// 它的主要作用是处理 co_yield 表达式产生的值,返回awaitable类型等待对象
//当协程执行到 co_yield 语句时,yield_value 函数会被调用,负责存储或处理这个通过 co_yield 返回的值,以便外部调用者能够获取
std::suspend_always yield_value(int value) {
current_value = value;
return {};
}
// void return_void() noexcept {
// std::cout << "return_void" << std::endl;
// _sleep();
// }
// 处理co_return:保存返回值
void return_value(int v) {
std::cout << "return_value=" << v << std::endl;
value = v;
}
void unhandled_exception() {
std::cout << "unhandled_exception" << std::endl;
std::terminate();
}
int current_value =222; // 存储协程返回值
};
handle_type h;
Task(handle_type h) : h(h) {
std::cout << "construct Task" << std::endl;
_sleep();
}
~Task() {
std::cout << "destroy Task" << std::endl;
_sleep();
if (h) h.destroy();
}
};
struct awaitFun {
//检查操作是否完成(初始为false,需要暂停,所以执行await_suspend)
bool await_ready() const noexcept {
std::cout << "await_ready,done=" << done << std::endl;
return done;
}
// 处理协程挂起逻辑,启动异步逻辑
void await_suspend(std::coroutine_handle<> h) noexcept {
std::cout << "await_suspend started" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));// 模拟异步操作,这里使用线程睡眠
result = 123;
done = true;
std::cout << "await_suspend end,ret=" << result << std::endl;
h.resume(); //操作完成,恢复协程
}
int await_resume() const noexcept {
std::cout << "await_resume,ret=" << result << std::endl;
return result;
} //恢复时调用,返回等待结果(T为结果类型)
bool done = false;
int result = 0;
};
Task testAwait() {
std::cout << "testAwait start" << std::endl;
int ret = co_await awaitFun();
std::cout << "testAwait end,ret=" << ret << std::endl;
}
int main() {
Task t = testAwait();//调用testAwait()时,首先调用get_return_object->接着调用initial_suspend->返回std::suspend_always暂停执行
std::cout << "Result: " << t.h.promise().value << std::endl; // 输出8
std::cout << "resume end" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(6));
return 0;
}
输出:
get_return_object
sleep 200
construct Task
sleep 200
initial_suspend (其返回suspend_never,所以才能继续往下执行,进入协程函数)
sleep 200
testAwait start
await_ready,done=0 (await_ready返回false,才执行await_suspend)
await_suspend started
await_suspend end,ret=123
await_resume,ret=123 (调用resume恢复协程,继续往下执行)
testAwait end,ret=123
final_suspend
sleep 200
Result: 222
resume end
destroy Task
sleep 200
用co_yield产生序列:
cpp
#include <iostream>
#include <coroutine>
#include <vector>
// 生成器返回类型
struct FibonacciGenerator {
struct promise_type;
using handle_type = std::coroutine_handle<promise_type>;
handle_type h;
FibonacciGenerator(handle_type h) : h(h) {}
~FibonacciGenerator() { if (h) h.destroy(); }
struct promise_type {
FibonacciGenerator get_return_object() {
return FibonacciGenerator(handle_type::from_promise(*this));
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() {}
// 用于存储生成的值
int current_value;
// 修正后的yield_value函数,接受int类型参数
std::suspend_always yield_value(int value) {
current_value = value;
return {};
}
};
bool move_next() {
if (h.done()) return false;
h.resume();
return!h.done();
}
int current() {
return h.promise().current_value;
}
};
// 协程函数
FibonacciGenerator fibonacci_generator(int limit) {
int a = 0;
while (a < limit) {
co_yield a++;
}
}
int main() {
FibonacciGenerator gen = fibonacci_generator(10);
while (gen.move_next()) {
std::cout << gen.current() << " ";
}
std::cout << std::endl;
return 0;
}
输出:0 1 2 3 4 5 6 7 8 9
协程文件读取:
cpp
#include <iostream>
#include <coroutine>
#include <thread>
#include <string>
#include <fstream>
class FileReader{
public:
struct promise_type;
using handle_type = std::coroutine_handle<promise_type>;
handle_type h_;
FileReader(handle_type h):h_(h)
{
}
~FileReader()
{
if(h_)
h_.destroy();
}
struct promise_type
{
FileReader get_return_object() {
return FileReader(handle_type::from_promise(*this));
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception() {}
// 用于存储生成的值
std::string curLine_;
// yield_value 函数处理 co_yield 产生的值
std::suspend_always yield_value(std::string &value) {
curLine_ = value;
return {};
}
};
bool next_line()
{
//执行co_return后,h_.done()将返回true
if (h_.done()) return false;
h_.resume();
return!h_.done();
}
std::string line(){
return h_.promise().curLine_;
}
};
bool filterOne(const std::string & line)
{
return line.find("error") != std::string::npos;
}
// 协程函数:按行读取文件并过滤
FileReader readFileAndFilter(const std::string& filename) {
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "无法打开文件: " << filename << std::endl;
co_return;
}
std::string line;
while (std::getline(file, line)) {
if (!filterOne(line)) {
co_yield line;
}
}
file.close();
co_return;//执行 co_return 后,FileReader.h.done() 将返回 true
}
int main()
{
FileReader reader = readFileAndFilter("./example.txt");
while (reader.next_line()) {
std::cout << reader.line() << std::endl;
}
return 0;
}