深度探索C++20协程机制

news2025/1/15 6:59:12
#include <iostream>
#include <coroutine>

class CoroTaskSub {
public:
	//编译器在处理协程函数时是通过其返回类型【即协程接口类型】,确定协程的承诺类型和协程句柄类型
	struct promise_type;
	using CoroHdl = std::coroutine_handle<promise_type>;

	//需要定义从协程句柄到接口对象的转换构造函数,否则协程函数会报错"无法从std::coroutine_handle<promise_type>转换到CoroTaskSub"
	CoroTaskSub(auto h)
		: hdl{ h } { 
	}
	 ~CoroTaskSub() {
		 if (hdl)
			 hdl.destroy(); // 协程句柄底层指针指向动态分配的内存,这部分内存需要显示手动释放
	}
private:
	CoroHdl hdl; // native coroutine handle
public:
	struct promise_type {
		auto get_return_object() { return CoroHdl::from_promise(*this); }//此处是通过承诺对象创建指向承诺对象的句柄对象,协程函数首次暂停返回给"调用方"时,创建的这个句柄对象会隐式转换到协程接口对象
		auto initial_suspend() { return std::suspend_always{}; }
		void return_void() { }
		void unhandled_exception() { std::terminate(); }
		auto final_suspend() noexcept { return std::suspend_always{}; }

		CoroHdl subHdl = nullptr;
	};

	//使得协程接口类型可做等待器
	bool await_ready() { return false; } // do not skip suspension
#if 0
	auto await_suspend(auto awaitHdl) {
		awaitHdl.promise().subHdl = hdl; // store sub-coroutine
		// 通过返回内部协程的句柄,执行代理直接提供到内部协程上,内部协程恢复执行;当内部协程在下一个暂停点挂起;执行代理被定位到此接口函数被返回后的流程
		//即不会重新在外部协程上重头执行co_await coro();因为执行代理的定位点不在那里,执行代理被定位到此函数返回之后的流程处;后面的流程就是执行代理被提供给main中【此函数不会被重复执行】
		//相当于内部协程程或者其他协程在此回调返回的间隙处被提供执行代理恢复执行
			return hdl; 
	}
#else
	void await_suspend(auto awaitHdl) {//awaitHdl传递的是当前协程的句柄,因为co_await coro();语句在外边协程中,所以此时awaitHdl为外部协程的局部
		// *this对象为内部协程函数的返回类型,所以其hdl成员存储的是内部协程的句柄;即将内部协程的句柄存储在外部协程的承诺对象中
		//这需要外部协程的承诺对象的存储内部协程句柄的成员的类型与内部协程句柄的类型一致;外部协程函数与内部协程函数同样的协程接口类型(返回类型)刚好可以满足这一点
		//这样就利用句柄的底层指针建立了一个协程状态链表结构
		awaitHdl.promise().subHdl = hdl;
	}
#endif
	 void await_resume() { }


	 //在main中调用,执行代理有时候给内部协程,有时候给外部协程=>类似于分发控制流
	 //这也侧面说明协程的执行绝不是传统的调用模型那样必须由调用方调用执行,执行流返回也需要返回给对应的调用方;
	 // 对于协程来说执行只是通过一个执行代理,而此执行代理由谁提供无所谓,二次执行时使用的执行代理是否是同一个也无所谓;
	 //协程严格来说没有所谓"调用方"的概念,协程只有执行代理的概念;
	 //使用协程时要摒弃调用堆栈的变化概念,取而代之的是执行代理的提供-转移-归还-保持-变换的概念
	 /*
	 * 执行代理的提供:协程句柄的resume接口提供
	 * 执行代理的转移:在等待器的await_suspend接口控制【返回某个其他协程的协程句柄时】
	 * 执行代理的归还:在等待器的await_suspend接口控制(归还执行代理给当初提供的"调用方")
	 * 执行代理的保持:在等待器的await_suspend接口控制(恢复当前暂停的协程)
	 * 执行代理的变换:在多次恢复协程执行的前后提供给该协程的执行代理并不一致【具体如何有效使用管理这种模式只有在深入应用协程后才能得出经验结论】
	 */

	 //所以这里外部协程与内部协程间仅仅是根据协程句柄-承诺对象结构建立一个链表关系【只是提供一个协程句柄的寻址数据结构,与用全局容器保存不同协程的句柄无本质区别(等价)】;除此外内外协程间再无其他特别关系,执行代理可以提供给外协程,内协程,main,甚至其他协程;
	 //所以说协程是独立的,没有从属关系

	 /*
	 * 进程:有父子(从属)关系
	 * 线程:没有父子关系,是分线程概念
	 * 协程:没有父子关系,独立概念;没有调用关系;只要执行代理的流转【执行代理的提供-转移-归还-保持】
	 */
	 bool resume() const {
		  if (!hdl || hdl.done()) 
			  return false; // nothing (more) to process
				 	 
		 // find deepest sub-coroutine not done yet:
		 CoroHdl innerHdl = hdl;
		 while (innerHdl.promise().subHdl && !innerHdl.promise().subHdl.done()) 
			innerHdl = innerHdl.promise().subHdl;

		 //第一次,因为句柄关联还未建立=>取到外部协程句柄,启动外部协程
		 // 协程句柄建立关联后,第二次会取到内部协程的句柄,调用其resume首次启动内部协程
		 //第三次会取到内部协程的句柄,调用其resume恢复内部协程
		 //第四次会取到外部协程的句柄,因为内部协程已经在最终暂停点暂停,调用其resume恢复外部协程
		 //第五次 按C++标准来说也会取到外部协程的句柄,因为内部协程已经在最终暂停点暂停,调用其resume恢复外部协程【但是由于msvc实现的缺陷,在noexcept声明的函数中引发异常导致程序终止】
		  innerHdl.resume(); 
		return !hdl.done();	 
	 }

};


CoroTaskSub coro()
{
	std::cout << " coro(): PART1\n";
	co_await std::suspend_always{}; // SUSPEND
	std::cout << " coro(): PART2\n";
}

CoroTaskSub callCoro()
 {
	 std::cout << " callCoro(): CALL coro()\n";
	 //内部协程函数的调用会创建内部协程的状态对象以及接口对象,内部协程的接口对象也用作外部协程在此处的等待器对象
	 //回调函数await_suspend的调用会把内部协程的句柄存储在当前协程句柄指向的承诺对象中;这就建立了关联
	 co_await coro(); // call sub-coroutine
	 std::cout << " callCoro(): coro() done\n";
	 co_await std::suspend_always{}; // SUSPEND
	 std::cout << " callCoro(): END\n";
 }

int main()
{
	//创建外部协程
	auto coroTask = callCoro(); 
	std::cout << "MAIN: callCoro() initialized\n";

	//这里coroTask.resume()首次被调用时,此时内部协程的句柄还未挂载到外部协程的承诺对象中;所以首次resume只恢复外部协程
	while (coroTask.resume()) // 
		std::cout << "MAIN: callCoro() suspended\n";		
	
	
	std::cout << "MAIN: callCoro() done\n";
	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2276876.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

linux手动安装mysql5.7

一、下载mysql5.7 1、可以去官方网站下载mysql-5.7.24-linux-glibc2.12-x86_64.tar压缩包&#xff1a; https://downloads.mysql.com/archives/community/ 2、在线下载&#xff0c;使用wget命令&#xff0c;直接从官网下载到linux服务器上 wget https://downloads.mysql.co…

Java Stream实现【Int / Long / Double / Bigdecimal】累计求和

文章目录 背景实现方案案例素材Int类型求和Long 类型求和Double 类型求和BigDecimal 类型求和 背景 在项目开发中经常会使用到数据统计&#xff0c;Java中有求和的方法&#xff0c;可使用Java的Stream工作流实现&#xff0c;记录下来&#xff0c;方便备查。 实现方案 可使用…

OFD文件纯前端查看解决方案

文章目录 ofd.js原有bug修复1、ofd格式文档打开报错2、签章信息不显示 效果展示源码下载 使用前请查看免责声明 ofd.js原有bug修复 1、ofd格式文档打开报错 原因分析&#xff1a; 文档打开时会解析所用到的字体信息&#xff0c;如果字体不在ofd.js预设字体时&#xff0c;会触…

使用 Docker 部署 Java 项目(通俗易懂)

目录 1、下载与配置 Docker 1.1 docker下载&#xff08;这里使用的是Ubuntu&#xff0c;Centos命令可能有不同&#xff09; 1.2 配置 Docker 代理对象 2、打包当前 Java 项目 3、进行编写 DockerFile&#xff0c;并将对应文件传输到 Linux 中 3.1 编写 dockerfile 文件 …

二手车交易系统的设计与实现(代码+数据库+LW)

摘 要 如今社会上各行各业&#xff0c;都喜欢用自己行业的专属软件工作&#xff0c;互联网发展到这个时候&#xff0c;人们已经发现离不开了互联网。新技术的产生&#xff0c;往往能解决一些老技术的弊端问题。因为传统二手车交易信息管理难度大&#xff0c;容错率低&#xf…

抖音ip属地没有手机卡会显示吗

在数字时代&#xff0c;社交媒体平台如抖音已成为人们日常生活的重要组成部分。随着抖音等应用对用户体验和隐私保护的不断优化&#xff0c;IP属地显示功能逐渐走进大众视野。这一功能旨在提高网络环境的透明度&#xff0c;打击虚假信息和恶意行为。然而&#xff0c;对于没有手…

springMVC---resultful风格

目录 一、创建项目 pom.xml 二、配置文件 1.web.xml 2.spring-mvc.xml 三、图解 四、controller 一、创建项目 pom.xml <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi…

[Git] 深入理解 Git 的客户端与服务器角色

Git 的一个核心设计理念是 分布式&#xff0c;每个 Git 仓库都可以既是 客户端&#xff0c;也可以是 服务器。为了更好地理解这一特性&#xff0c;我们通过一个实际的 GitHub 远程仓库和本地仓库的场景来详细说明 Git 如何在客户端和服务器之间协作&#xff0c;如何独立地进行版…

网络安全-RSA非对称加密算法、数字签名

数字签名非常普遍&#xff1a; 了解数字签名前先了解一下SHA-1摘要&#xff0c;RSA非对称加密算法。然后再了解数字签名。 SHA-1 SHA-1&#xff08;secure hash Algorithm &#xff09;是一种 数据加密算法。该算法的思想是接收一段明文&#xff0c;然后以一种不可逆的方式将…

了解 ASP.NET Core 中的中间件

在 .NET Core 中&#xff0c;中间件&#xff08;Middleware&#xff09; 是处理 HTTP 请求和响应的核心组件。它们被组织成一个请求处理管道&#xff0c;每个中间件都可以在请求到达最终处理程序之前或之后执行操作。中间件可以用于实现各种功能&#xff0c;如身份验证、路由、…

【三维数域】三维数据调度-负载均衡和资源优化

在处理大规模三维数据时&#xff0c;负载均衡和资源优化是确保系统高效运行、提供流畅用户体验的关键。这两者不仅影响到系统的性能和稳定性&#xff0c;还直接决定了用户交互的质量。以下是关于如何在三维数据调度中实现有效的负载均衡和资源优化的详细探讨。 一、负载均衡 负…

AI大模型开发—1、百度的千帆大模型调用(文心一言的底层模型,ENRIE等系列)、API文档目的地

文章目录 前言一、千帆大模型平台简介二、百度平台官网初使用1、平台注册和使用2、应用注册 并 申请密钥3、开启千帆大模型 API调用a、API文档b、 前言 本章旨在为读者奉献一份实用的操作指南&#xff0c;深入探索如何高效利用百度千帆大模型平台的卓越功能。我们将从账号注册…

Java Stream流操作List全攻略:Filter、Sort、GroupBy、Average、Sum实践

在Java 8及更高版本中&#xff0c;Stream API为集合处理带来了革命性的改变。本文将深入解析如何运用Stream对List进行高效的操作&#xff0c;包括筛选&#xff08;Filter&#xff09;、排序&#xff08;Sort&#xff09;、分组&#xff08;GroupBy&#xff09;、求平均值&…

《视听导报》是什么类型的报纸?报纸上发文章要交版面费吗?

作为个人成果发表的重要场所&#xff0c;报纸目前正得到越来越多单位的认可。不过在投稿时&#xff0c;我们既要考虑投稿的报纸是否符合评审标准&#xff0c;也要考虑发表文章的成本是否在我们的承受范围之内。 下面就让我们以《视听导报》为例&#xff0c;了解下如何查看报纸的…

candb++ windows11运行报错,找不到mfc140.dll

解决问题记录 mfc140.dll下载 注意&#xff1a;放置位置别搞错了

服务器引导异常,Grub报错: error: ../../grub-core/fs/fshelp.c:258:file xxxx.img not found.

服务器引导异常,Grub报错: error: ../../grub-core/fs/fshelp.c:258:file xxxx.img not found. 1. 故障现象2. 解决思路3. 故障分析4. 案件回溯5. 解决问题 1. 故障现象 有一台服务器业务报无法连接. 尝试用Ping命令发现无法ping通. 通过控制台查看发现有以下报错: error: ..…

LeetCode第432场周赛 (前3题|多语言)

比赛链接&#xff1a;第432场周赛 文章目录 3417. 跳过交替单元格的之字形遍历思路代码CJavaPython 3418. 机器人可以获得的最大金币数思路代码CJavaPython 3419. 图的最大边权的最小值思路代码CJavaPython 总结 3417. 跳过交替单元格的之字形遍历 思路 没啥好说的就是模拟 按…

下载导出Tomcat上的excle文档,浏览器上显示下载

目录 1.前端2.Tomcat服务器内配置3.在Tomcat映射的文件内放置文件4.重启Tomcat&#xff0c;下载测试 1.前端 function downloadFile() {let pictureSourceServer "http://192.168.1.1:8080/downFile/";let fileName "测试文档.xlsx";let fileURL pictu…

蓝桥杯备赛:顺序表和单链表相关算法题详解(上)

一.询问学号&#xff08;顺序表&#xff09; 1.题目来源&#xff1a; https://www.luogu.com.cn/problem/P3156 &#xff08;洛谷原题&#xff09; 2.解析与代码实现&#xff1a; &#xff08;1&#xff09;解析&#xff1a; 首先结合题目和输出样例不难看出这道题目是围绕两…

Java 基于微信小程序的高校科研团队管理系统设计与实现(附源码,部署,文档

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…