【C++学习(35)】在Linux中基于ucontext实现C++实现协程(Coroutine),基于C++20的co_await 协程的关键字实现协程

文章目录

  • 为什么使用协程
  • 协程的理解
  • 协程优势
  • 协程的原语操作
    • yield 与 resume 是一个switch操作(三种实现方式):
  • 基于 ucontext 的协程
    • 基于 XFiber 库的操作
      • 1 包装上下文
      • 2 XFiber 上下文调度器
        • 2.1 CreateFiber
        • 2.2 Dispatch
  • 基于C++20的co_return 协程的关键字实现协程
  • 参考

为什么使用协程

从性能方面来看,对于使用异步 io 的线程,存在三个问题:

  • 系统线程占用大量的内存空间
  • 线程切换占用大量的系统时间
  • 为了线程安全,线程间需要加锁保护资源,降低执行的效率

从编程角度来看,无论同步还是异步编程方式,都是基于事件驱动的。事件驱动流程包括注册事件,绑定回调,触发回调,提高了系统的并发。但是由于回调的多层嵌套,使得编程复杂,降低了代码的可维护性。

在资源有限的前提下,高性能服务需要解决的问题有:

  • 减少线程的重复高频创建:线程池
  • 尽量避免线程的阻塞
  • Reactor + 非阻塞回调:解决问题的能力有限
  • 响应式编程:容易陷入回调地狱,割裂业务逻辑
  • 协程:将同 io 转成异步 io
  • 提升代码的可维护与可理解性:减少回调函数,减少回调链深度

而协程的出现,可以很好地解决上述问题。

协程的理解

协程(Coroutine)是一种能够挂起个恢复的函数过程 是一种轻量级的并发编程方式,也称为用户级线程。它与传统的线程(Thread)相比,具有更低的开销和更高的执行效率。 协程通常运用在异步调用中。

协程运行在线程之上。当一个协程调用阻塞 io,主动让出 cpu ( yield 原语) ,让另一个协程运行在当前线程之上( resume 原语)。协程没有增加线程数量,只是在线程的基础上通过分时复用的方式运行多个协程,降低了系统内存。而且协程的切换在用户态完成,减少了系统切换开销。

协程优势

消耗系统资源和切换代价更小
协程可以实现无锁编程
简化了异步编程,可以达到以同步的编程方式实现异步的性能。

  • 协程适用于 I/O 密集型业务,线程切换频繁。其他情况,性能不会有太大的提升。

协程的原语操作

  • yield: 协程主动让出CPU给调度器。时机:业务提交 -> epoll_wait
  • resume: 调度器恢复协程的运行权。时机:epoll_wait -> 业务处理
  • resume 和 yield 是两个可逆的原子操作。

yield 与 resume 是一个switch操作(三种实现方式):

  • 1.longjump/setjump
  • 2.ucontext
  • 3.汇编实现

基于 ucontext 的协程

协程的实现与线程的主动切换有关,当“当前上下文”可能阻塞时,需要主动切换到其它上下文来避免操作系统将当前线程挂起从而降低效率。

在Linux中定义了ucontext_t结构体来表示线程的上下文结构。

typedef struct ucontext_t {struct ucontext_t *uc_link;//表示当当前上下文阻塞时会被切换的上下文。sigset_t           uc_sigmask;//被当前线程屏蔽的信号stack_t 					 uc_stack;//线程栈mcontext_t 				 uc_mcontext;//与机器相关的线程上下文的表示
} ucontext_t;

与上下文相关的有四个函数:

    getcontext(ucontext_t* ucp): 调用后基于当前上下文初始化ucp所指向的上下文结构体。setcontext(const ucontext_t* ucp): 切换到ucp所指向的上下文,如果调用成功则不会返回,因为上下文已经被切换。makecontext(ucontext_t* ucp, void (*func)(), int argc, ...): 用于指定上下文需要执行的函数,要求在调用之前context已经确定栈和 uc_link. 当切换到该上下文后,函数func就会被执行。函数返回后,后继线程就会被切换到,如果uc_link为NULL,则线程退出。swapcontext(ucontext_t* restrict oucp, const ucontext_t* restrict ucp): 将当前上下文保存到oucp中,然后切换到ucp对应的上下文中。与setcontext的区别在于是否保存当前上下文。

附上stack_t的定义:

typedef struct {void* ss_sp;int ss_flags;size_t ss_size;
} stack_t;

比如可以通过下面的程序实现循环打印:

#include <ucontext.h>
#include <unistd.h>
#include <stdio.h>int main() {int idx = 0;ucontext_t ctx1;getcontext(&ctx1);printf("%d\n", idx);idx++;sleep(1);setcontext(&ctx1);return 0;
}

基于 XFiber 库的操作

https://github.com/HiYx/xfiber

以XFiber为例讲解一下一个轻量级协程库的基本实现方式。

1 包装上下文

Linux提供的协程结构体比较简陋,并不足以供协程库使用,因此需要进行包装一下。

struct Fiber {uint64_t seq_;XFiber* xfiber_;std::string fiber_name_;ucontext_t ctx_;uint8_t* stack_ptr_;size_t stack_size_;std::function<void()> run_;WaitingEvents waiting_events_;
};

其中XFiber为协程的调度器,后面会讲。WatingEvents为协程所需要等待的读和写的文件描述符。

struct WaitingEvents {std::vector<int> waiting_fds_w_;std::vector<int> waiting_fds_r_;int64_t expire_at_;
};

2 XFiber 上下文调度器

2.1 CreateFiber

先从最基本的创建一个协程开始,首先注意协程和线程的区别,协程代表一段可以分开执行的逻辑,但是和其它协程还是保持串行执行,因此协程创建并不会马上执行,而是由协程调度器统一执行。

先看看创建协程函数的签名:

void XFiber::CreateFiber(std::function<void()> run, size_t stack_size, std::string fiber_name);

run即要执行的函数,这里作者设定了只能是无参数、无返回值的函数类型,但是其实可以借助C++模板实现各种类型函数的注册。

协程调度器主要维护两个协程队列,分别是运行队列和就绪队列,运行队列中的协程会被切换到,而就绪队列中的协程会在下一次的循环中被切换到。

同时维护两个map,io_waiting_fibers_表示监听的文件描述符所对应的一对读和写的协程,expire_fibers_的value为一个有序集合,表示在某个时间点会超时的协程集合。

2.2 Dispatch

当Dispatch函数开始运行时,各协程才开始运行。该函数主要分为三个部分:

  • 处理已经就绪的协程。将就绪队列move到运行队列中,然后将就绪队列清空,这样做的原因是这一循环的就绪队列在运行中可能重新回到就绪队列中(主动Yield就会回到就绪队列)。

  • 协程切换过程就涉及到上面的swapcontext函数,为了使得协程能够在返回后能够重新回到XFiber中,其结构体中维护了一个sched_ctx_成员表示调度器的上下文。因此每一条Fiber在被创建时都将sched_ctx_作为接下来切换到的上下文,这样就保证了每一条协程在执行完成以后都能过回到调度器来,并由调度器处理接下来的就绪协程。

  • 检查超时的协程,对于超时的协程集合,需要将这些协程通过 WakeupFiber 函数进行唤醒。

  • 调用 epoll 相关方法,检查所有的epoll事件,并唤醒相关协程。

基于C++20的co_return 协程的关键字实现协程

co_return :co_return 是 C++20 中引入的关键字,用于在协程中返回结果或结束协程。它用于替代 return 关键字,在协程函数中表示返回值,并触发协程的完成。

main函数创建了一个进程, 进程里面创建了一个主线程,然后执行每个函数就是子线程。

  • 先进入bar()函数, 先执行call bar ,
  • 然后执行before bar 经过挂起点然后挂起,
  • 这时候一个线程跳出了bar函数, 到main里面,
  • 另一个线程执行fool函数,
  • fool函数执行完以后, 再回到bar 函数里面继续执行

这就是本代码的大概思路

在这里插入图片描述

参考

https://blog.csdn.net/txh1873749380/article/details/134174067
https://www.cnblogs.com/kaleidopink/p/16387004.html
https://blog.csdn.net/m0_74036006/article/details/135960299

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

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

相关文章

844.比较含退格的字符串

java用 O&#xff08;1&#xff09;空间这个方法&#xff0c;容易挺多bug的… O&#xff08;1&#xff09;空间 #&#xff1a;删除前一个字符 》 从后面开始判断&#xff08;这样可以用跳过的思想&#xff09;不能使用两次 i- - 来处理 # 的操作&#xff0c;会造成误删了前面…

大数据实训室建设的必要性

一、大数据发展的背景 大数据作为当今信息技术领域的核心驱动力&#xff0c;正在深刻地改变着社会的各个方面。它不仅仅是指数据量庞大&#xff0c;更重要的是指数据的多样性、实时性和复杂性。随着云计算、物联网等技术的迅猛发展&#xff0c;大数据已成为推动经济社会发展的…

MyBatis——增删查改(XML 方式)

1. 查询 1.1. 简单查询 使用注解的方式主要是完成一些简单的增删查改功能&#xff0c;如果要实现复杂的 SQL 功能&#xff0c;还是建议使用 XML 来配置映射语句&#xff0c;将 SQL 语句写在 XML 配置文件中 如果要操作数据库&#xff0c;需要做以下的配置&#xff0c;与注解…

K8S如何基于Istio实现全链路HTTPS

K8S如何基于Istio实现全链路HTTPS Istio 简介Istio 是什么?为什么选择 Istio?Istio 的核心概念Service Mesh(服务网格)Data Plane(数据平面)Sidecar Mode(边车模式)Ambient Mode(环境模式)Control Plane(控制平面)Istio 的架构与组件Envoy ProxyIstiod其他组件Istio 的流量管…

51c大模型~合集44

我自己的原文哦~ https://blog.51cto.com/whaosoft/11884382 #DR4SR 最佳学生论文解读&#xff0c;中科大、华为诺亚&#xff1a;序列推荐新范式DR4SR 本工作由认知智能全国重点实验室 IEEE Fellow 陈恩红团队与华为诺亚方舟实验室完成。陈恩红教授团队深耕数据挖掘、机器学…

HCIP-快速生成树RSTP

一、RSTP是什么 STP&#xff08;Spanning Tree Protocol &#xff09;是生成树协议的英文缩写。该协议可应用于环路网络&#xff0c;通过一定的算法实现路径冗余&#xff0c;同时将环路网络修剪成无环路的树型网络&#xff0c;从而避免报文在环路网络中的增生和无限循环。 RS…

在Element Ui中支持从系统粘贴版中获取图片和PDF,Docx,Doc,PPT等文档

在上一篇中&#xff0c;我们单纯的实现了Ctrl V实现从粘贴版中获取图片信息&#xff0c;但是点击上传的时候会有个bug&#xff0c;就是点击文件上传的时候&#xff0c;会出现一个bug&#xff0c;这篇&#xff0c;我们将在上一篇的基础上进行完善&#xff0c;并支持从粘贴版中获…

《数据可视化技术》上机报告

一、实验目的及要求 掌握pyecharts数据可视化环境搭建以及pyecharts交互式基础图形的绘制。 &#xff08;1&#xff09;掌握pyecharts中初始配置项&#xff0c;系列配置项&#xff0c;全局配置项的配置方法。 &#xff08;2&#xff09;掌握pyecharts中条形图的绘制方法。 …

️虚拟机配置NAT和Bridge模式

虚拟机的网络配置 桥接 通过使用物理机网卡 具有单独ip NAT 把物理机为路由器进行上网 NAT模式&#xff1a; 所谓nat模式&#xff0c;就是虚拟系统会通过宿主机的网络来访问外网&#xff0c;而这里的宿主机相当于有两个网卡&#xff0c;一个是真实网卡&#xff0c;一个是虚拟…

2023年值得关注的9大零售趋势

图片来源&#xff1a;Photo by Heidi Fin on Unsplash 随着经济衰退的威胁日益迫近&#xff0c;新的一年带给零售商一系列挑战&#xff0c;而后者刚从一年的供应链瓶颈和库存过剩中恢复过来。当然&#xff0c;2023年并非一直悲观。随着越来越多的零售商找到新的机会&#xff0c…

杰控通过 OPCproxy 获取数据发送到服务器

把数据从 杰控 取出来发到服务器 前提你在杰控中已经有变量了&#xff08;wincc 也适用&#xff09; 打开你的opcproxy 软件包 opcvarFile 添加变量 写文件就写到 了 opcproxy.ini中 这个文件里就是会读取到的数据 然后 opcproxy.exe发送到桌面快捷方式再考回来 &#…

DVWA靶场通关——SQL Injection篇

一&#xff0c;Low难度下unionget字符串select注入 1&#xff0c;首先手工注入判断是否存在SQL注入漏洞&#xff0c;输入1 这是正常回显的结果&#xff0c;再键入1 You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for…

鸿蒙学习生态应用开发能力全景图-三方库(3)

鸿蒙生态三方库&#xff0c;是在鸿蒙系统上可重复使用的软件库&#xff0c;可帮助开发者重用技术资产&#xff0c;快速开发鸿蒙生态应用、元服务&#xff0c;提升开发效率。根据不同的开发语言分为两种&#xff1a;  ArkTS/TS/JS 语言的三方库&#xff0c;可直接导入并使用。…

OpenAI官方发布:利用ChatGPT提升写作的12条指南

近日&#xff0c;OpenAI官方发布了学生如何利用ChatGPT提升写作的12条指南&#xff0c;值得深入研究学习。 在如今AIGC应用爆发增长的时间点&#xff0c;如何充分利用生成式AI工具&#xff0c;如ChatGPT&#xff0c;有效切快速的提升写作和学习能力&#xff0c;成为每个学生、…

【数据库系列】Spring Data Neo4j Cypher 查询使用进阶指南

在 Neo4j 中&#xff0c;Cypher 查询语句并不像 MySQL 的 mapper XML 那样直接支持拆分和组织。然而&#xff0c;你可以使用一些策略来管理和重用 Cypher 查询&#xff0c;使其更易于维护和组织。以下是几种方法&#xff1a; 1. 使用 Spring Data Neo4j 的 Repository 接口 通…

.net core开发windows程序在国产麒麟操作系统中运行

.net core自从3.1版本号后&#xff0c;完全是一个独立的开源的多平台开发组件&#xff0c;目前国产化是趋势&#xff0c;不少项目需要开发国产如Kylin操作系统中运行的程序&#xff0c;无论是Web程序还是桌面程序&#xff0c;都有这样的需求。 首先&#xff0c;可明确的的.net…

基于 Python 的 Bilibili 评论分析与可视化

一、项目概述 本项目利用 Python 对 Bilibili &#xff08;哔哩哔哩&#xff09;平台上的视频评论数据进行爬取、清洗和分析&#xff0c;并通过可视化展示数据的主要特征。我们通过以下几个步骤实现了这一过程&#xff1a; 数据爬取&#xff1a;使用 Bilibili 提供的 API 获取…

如何禁用VMware虚拟网卡

安装VMWare虚拟机之后&#xff0c;会在本地创建两个虚拟网卡VMware Network Adapter VMnet1和VMware Network Adapter VMnet8&#xff0c;如果使用iNode客户端联网时会进行禁用多网卡检测&#xff0c;否则无法联网。因此&#xff0c;问题根源就在于虚拟网卡未禁用。 1、网络和…

ElasticSearch备考 -- Cross cluster replication(CCR)

一、题目 操作在cluster1&#xff08;local&#xff09;中操作索引task&#xff0c;复制到cluster2&#xff08;remote&#xff09;中 二、思考 CCR 我们可以对标MySQL 理解为为主从&#xff0c;后者备份。主节点负责写入数据&#xff0c;从/备节点负责同步时主节点的数据。 …

界面控件DevExpress WPF中文教程:TreeList视图及创建分配视图

DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序&#xff0c;这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 无论是Office办公软件…