C++ 20 Coroutines 使用介绍

C++ 20 Coroutines 使用介绍

协程介绍

  1. 协程是单线程的: 在go中因为其语言底层用了几条线程执行不同的协程, 使得协程看起来像多线程的,但在c++中协程是单线程的, 当然你也可以利用c++提供的协程功能自己实现多线程协程.
  2. 协程的关键在于执行权在不同函数间交换, 并在执行权交换时进行一些数据交流.
pipe: 一个假设的管道

func 1(){
	函数块1: {done someting ...}
	(execution, data) -> pipe		// 提交执行权, 数据
	(execution, data) <- pipe		// 请求执行权, 数据
	函数块2: {done someting ...}
	...
}

func 2(){
	(execution, data) <- pipe		// 请求执行权, 数据
	函数块3: {done someting ...}
	(execution, data) -> pipe		// 提交执行权, 数据
	(execution, data) <- pipe		// 请求执行权, 数据
	函数块4: {done someting ...}
	...
}

int main(){
	func 1();
	func 2();
}

执行顺序如下:

函数块1 -> 函数块3 -> 函数块2 -> 函数块4

C++协程逻辑

为了完成以上功能, C++, 定义了自己的一整套协程逻辑

  1. 协程分为协程调用者和协程执行者, 他们之间的角色是固定的, 执行权只能有他们之间传递, 而不是随意的传递, 这一点有点类似函数的栈, 比如执行权由A->B-> C, 这时候如果要回到A, 那么执行权的传递也必须是C->B->A, 而不能直接 C->A.

  2. 协程数据, 分配在堆区, 在定义协程函数时, 通过一个协程对象, 将函数中所有的局部变量寄付在堆区

协程对象

一个协程对象的完整结果如下

struct action{											// 名称任意, 系统库提供了suspend_never, suspend_always, suspend_if可供调用
	bool await_ready() noexcept{return false;}			// 必须实现此接口
	void await_suspend(coroutine_handle<>) noexcept	{}	// 必须实现此接口, 可通过此处在函数内部获取到handle
	void await_resume() noexcept{}						// 必须实现此接口
}

template<typename ToOut, typename ToIn>		// 非必须
struct coroutine_name{						// 名称任意
	struct promise_type{					// 名称必须为promise_type
		Toout _to_out;						// 非必须, 名称任意
		ToIn _to_in;						// 非必须, 名称任意
		promise_type() = default;			// 非必须
		~promise_type() = default;			// 非必须
		
		coroutine_name get_return_object(){return coroutine_name{*this};}	// 必须实现此接口
		auto initial_suspend();				// 必须实现此接口, 返回值必须为类似action的struct
		auto final_suspend();				// 必须实现此接口, 返回值必须为类似action的struct
		void unhandled_exception(){::std::terminate()} // 必须实现此接口, 用于处理协程函数内部抛出错误
		auto yield_value(ToOut val)			// 如果协程函数内部有关键字co_yield则必须实现此接口, 返回值必须为类似action的struct
		void return_void();					// 如果协程函数内部无关键字co_return则必须实现此接口
		void return_value(ToOut val){_to_out = val;} // 如果协程函数内部有关键字co_return则必须实现此接口
	}
	
	coroutine_handle<promise_type> handle;	// 非必须, 但一般均需实现, 名称随意, 提供给外面的handle
	coroutine_name(promise_type &p): handle(coroutine_handle<promise_type>::from_promise(p)){};
}

coroutine_name func(){						// 协程函数
	co_await suspend_always{};
	co_yield val;
	co_return val;
}


auto operator co_await(val_type& val) noexcept {
    do something ... 
    return action{};
}

int main(){
	auto f = func();
	f.handle.resume();				//用得最多
	f.handle.promise()._to_out;		//用得较多
	f.handle.done();				//用得较少
	f.handle.destory();				//一般不用
}

关键字与调用顺序

  1. 执行promise_type() 产生一个promise对象
  2. 通过promise对象, 执行get_return_object(), 产生一个coroutine_name对象, 并记录handle
  3. 执行initial_suspend(), 根据返回值, 判断是否立即执行协程函数, 当返回值中await_ready()返回值为ture则立即执行协程函数, 否则跳出到主函数
  4. f.handle.resume() 将执行权传递给协程函数
  5. f.handle.promise().xxx 通过handle查找promise的属性
  6. co_await xxx, 根据xxx判断是否将执行权传递给主函数, 关键字可通过重载使得xxx可以是任意类型
  7. co_yield val, 会执行yield_value(val)函数, 并根据放回值判断是否将执行权传递给主函数, co_yield VALUE 就是相当于 co_await p.yield_value(VALUE)
  8. co_return val, 会执行return_value(val)函数, 如果没有co_return, 则会在协程函数最后执行return_void函数
  9. 在return_value或return_void之后会执行final_suspend()函数, 根据放回值判断是否立即析构promise函数, 注意promise析构后主函数无法通过promise获得值
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值