1、协程概念
协程是一种比函数更轻量的子程序,它可以在执行过程中挂起(yield),保存当前状态,然后在稍后从同一位置恢复执行。与线程不同,协程共享栈空间,切换成本低,非常适合处理高并发、IO密集型任务。
协程描述为一个函数的泛化(generalisation)。与普通函数相比,除了“调用”和“返回”操作外,它还有3个额外的操作 - “暂停”,“恢复”和“摧毁”。
2、协程使用场景
网络编程:协程非常适合处理网络请求、文件读写等,能够简化异步I/O操作。
游戏开发:在游戏开发中,协程可以用于处理游戏逻辑、动画和事件。
数据处理:协程可以用于处理大规模数据流,逐步生成和处理数据,如std::generator。
3、C++协程
协程关键组件
一个函数的返回值类型如果是符合协程的规则的类型,那么这个函数就是一个协程(协程体)。而co_await、co_yield、co_return这些运算符在这种函数中使用,即这个协程中使用。
符合协程的规则的类型称为协程函数~~
- 协程体:使用co_await、co_return、co_yield关键字标记的特殊函数。
- 承诺对象(Promise):协程的状态对象,包含了协程的结果、暂停和恢复的逻辑。
- 协程句柄(Handle):启动协程的实体,如std::coroutine_handle。
- "协程函数"(Coroutine Functions):自定义promise_type实现协程句柄std::coroutine_handle <promise_type>,如generator、task 和 lazy。
- awaiter:实现了std::suspend_always、std::suspend_never或自定义等待行为的类,用于控制协程的挂起和恢复。
- co_await:用于等待一个异步操作的完成。这个关键词被用于挂起当前协程,并等待另一个操作完成。“挂起”(suspend)这个术语在协程中指的是保存当前协程的状态,然后将控制权交给调用者(或者调度器)。"等待"指的是当前协程在此位置的执行需要一个操作的完成。
- co_yield:用于生成一个值并暂停协程的执行。个关键词将一个值返回给协程的调用者,并挂起当前协程。如果我们把协程想象成一个可以多次返回的函数,那么 co_yield 就类似于 return,但它允许我们在返回后,再从它挂起的地方继续执行。
- co_return:用于返回一个值并结束协程的执行。与 return 在普通函数中的作用相似,co_return 用于从协程中返回值。但不同的是,co_return 还表示了协程的结束。
coroutine_handle promise_type
std::coroutine_handle 类模板是为我们实现协程的各种“魔法”提供支持的最底层的设施,其主要负责存储协程的句柄。它分为模板 std::coroutine_handle<Promise> 和模板特化 std::coroutine_handle<void> ,std::coroutine_handle<void> 可以简单理解为就是做了类型擦除的 std::coroutine_handle<Promise>
自定义promise_type实现std::coroutine_handle<promise_type>
using Handle = std::coroutine_handle<promise_type>;
C++协程代码实现
基本协程框架
vs上支持coroutine实现
#include<coroutine>
#include<iostream>
class Coro {
};
Coro myCoroutine() {
std::cout << " coroutine !" << std::endl;
co_return;
}
int main()
{
auto co = myCoroutine();
return 0;
}
可以根据提示写出定义一下内容:
//协程的返回类型:std::suspend_always 和 std::suspend_never:用于控制协程的暂停行为
#include<coroutine>
#include<iostream>
class Coro {
public:
struct promise_type {
std::suspend_never initial_suspend() {
return{};
}
std::suspend_always final_suspend() noexcept {
return{};
}
void unhandled_exception() {}
Coro get_return_object() {
return Coro{};
}
void return_void() {}
};
};
Coro myCoroutine() {
std::cout << " coroutine !" << std::endl;
co_return;
}
int main()
{
auto co = myCoroutine();
return 0;
}
自定义promise_type实现std::coroutine_handle<promise_type>
#include<coroutine>
#include<iostream>
class Coro {
public:
struct promise_type {
std::suspend_never initial_suspend() {
return{};
}
std::suspend_always final_suspend() noexcept {
return{};
}
void unhandled_exception() {}
Coro get_return_object() {
return handle_type::from_promise(*this);
}
void return_void() {}
};
// 自定义promise_type实现std::coroutine_handle<promise_type>
using handle_type = std::coroutine_handle<promise_type>;
handle_type coh;
Coro(handle_type h):coh(h) {}
~Coro() {
coh.destroy();
}
};
Coro myCoroutine() {
std::cout << " coroutine !" << std::endl;
co_return;
}
int main()
{
auto co = myCoroutine();
return 0;
}
实现斐波那契数列
#include<coroutine>
#include<iostream>
class Fib {
public:
struct promise_type {
std::suspend_never initial_suspend() {
return{};
}
std::suspend_always final_suspend() noexcept {
return{};
}
void unhandled_exception() {}
Fib get_return_object() {
return handle_type::from_promise(*this);
}
void return_void() {}
//定义co_yield,生成下一个值
std::suspend_always yield_value(int v) {
if (v == 0 || v == 1) {
cur = 1;
}
else {
int tmp = pre;
pre = cur;
cur = pre + tmp;
}
return {};
}
int pre = 1;
int cur = 1;
};
// 自定义promise_type实现std::coroutine_handle<promise_type>
using handle_type = std::coroutine_handle<promise_type>;
handle_type coh;
Fib(handle_type h) :coh(h) {}
~Fib() {
coh.destroy();
}
bool cur() {
if (coh.done()) { return false; }
std::cout << value() << std::endl;
coh.resume();
return !coh.done();
}
int value() {
return coh.promise().cur;
}
};
Fib fib(int n) {
for (int i = 0; i <= n; i++) {
co_yield i;
}
}
int main()
{
auto f = fib(12);
while (f.cur()) {
}
return 0;
}
Generator:C++23支持
Awaiter(等待体)
根据 Awaitable 和 Awaiter 概念实现和定义运算符 co_await
Awaiter 类型是一种实现三个特殊方法的类型,它们被称为 co_await 表达式的一部分:await_ready,await_suspend和await_resume。
Awaitable 和 Awaiter 概念
Awaitable 接口指定控制 co_await 表达式语义的方法。当一个值为 co_await 时,代码被转换为对 awaitable 对象上的方法的一系列调用。它可以指定:是否暂停当前协程,暂停调度协程以便稍后恢复后执行一些逻辑,还有在协程恢复后执行一些逻辑以产生 co_await 表达式的结果。
co_await运算符是一个新的一元运算符,可以应用于一个值。例如:co_await someValue。
co_await 运算符只能在协程的上下文中使用。这有点语义重复,因为根据定义,任何包含 co_await 运算符的函数体都将被编译为协程。
支持 co_await 运算符的类型称为 Awaitable 类型。
template<typename P, typename T>
decltype(auto) get_awaitable(P& promise, T&& expr)
{
if constexpr (has_any_await_transform_member_v<P>)
return promise.await_transform(static_cast<T&&>(expr));
else
return static_cast<T&&>(expr);
}
template<typename Awaitable>
decltype(auto) get_awaiter(Awaitable&& awaitable)
{
if constexpr (has_member_operator_co_await_v<Awaitable>)
return static_cast<Awaitable&&>(awaitable).operator co_await();
else if constexpr (has_non_member_operator_co_await_v<Awaitable&&>)
return operator co_await(static_cast<Awaitable&&>(awaitable));
else
return static_cast<Awaitable&&>(awaitable);
}
定义 Awaiter 类型
首先,它需要知道它将等待哪个 async_manual_reset_event 的对象,因此它需要一个对这一事件和对应构造函数的应用来进行初始化。
它还需要充当 awaiter 值链表中的节点,因此它需要持有指向列表中下一个 awaiter 对象的指针。
它还需要存储正在执行 co_await 表达式的等待协程的coroutine_handle,以便在事件变为 'set' 状态时事件可以恢复协程。我们不关心协程的 promise 类型是什么,所以我们只使用 coroutine_handle <>(这是 coroutine_handle <void> 的简写)。
最后,它需要实现 Awaiter 接口,因此需要三种特殊方法:await_ready,await_suspend 和 await_resume。 如果我们不需要从 co_await 表达式返回一个值,因此 await_resume 可以返回 void。
#include <iostream>
#include <coroutine>
#include <thread>
#include <chrono>
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() {}
};
struct Coroutine {
struct promise_type {
Coroutine get_return_object() {
return Coroutine{ handle_type::from_promise(*this) };
}
std::suspend_never initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() { std::terminate(); }
void return_void() {}
};
using handle_type = std::coroutine_handle<promise_type>;
handle_type coro_handle;
Coroutine(handle_type h) : coro_handle(h) {}
~Coroutine() { coro_handle.destroy(); }
};
Coroutine asyncTask() {
std::cout << "Waiting for 1 second..." << std::endl;
//std::suspend_always、std::suspend_never、自定义awaiter类
co_await Awaiter();
//co_await std::suspend_always{};
std::cout << "Done waiting!" << std::endl;
}
int main() {
auto task = asyncTask(); // 创建协程
std::this_thread::sleep_for(std::chrono::seconds(2)); // 等待协程完成
//task.coro_handle.resume();
return 0;
}