Reactor 和 Proactor模式,IO复用与epoll、同步IO,异步IO与协程

汽车软件中的CPU密集与IO密集任务

在汽车软件中,涉及到ADAS的长期占用CPU的计算任务可以算的上是CPU密集型。

另外的,众多SOA原子服务或者各种数据收集、处理、分发、log系统,应该算是IO密集型任务。

寻求一些手段优化IO性能的原因

在过去开发应用或者中间件时,使用Linux提供的接口,例如直接socket,一般就两种模式,实质上是使用了同步IO:

1、开启循环子线程,阻塞在socket接收处。

2、线程开启循环,周期轮询socket。

这种方式造成了一些弊端,例如对每个socket都需要维护一个子线程,系统给每个线程分配资源造成了资源浪费(如内存)。
虽然阻塞不占用CPU时间,但是如果存在大量socket子线程,线程调度会花费很多CPU时间在进程切换中。造成系统CPU负载率高。

潜在的解决方案

经过研究,一些关键词引起关注。正如本文的标题。

Reactor 和 Proactor模式

Reactor模式和Proactor模式都是基于事件驱动的设计模式,用于处理高并发环境下的I/O多路复用问题。然而,它们在处理I/O事件的方式上有所不同。

Reactor模式是非阻塞同步网络模式,它感知的是就绪可读写事件。具体来说,当某个I/O事件(例如可读就绪)发生时,需要应用进程主动调用相应的read方法来完成数据的读取。这个过程是同步的,即读取完数据后应用进程才能处理数据。因此,Reactor模式可以理解为“来了事件操作系统通知应用进程,让应用进程来处理”。

Proactor模式则是异步网络模式,它感知的是已完成的读写事件。在发起异步读写请求时,需要传入数据缓冲区的地址等信息,这样系统内核才可以自动帮我们把数据的读写工作完成。这里的读写工作全程由操作系统来做,并不需要像Reactor那样还需要应用进程主动发起read/write来读写数据。操作系统完成读写工作后,就会通知应用进程直接处理数据。因此,Proactor模式可以理解为“来了事件操作系统来处理,处理完再通知应用进程”。

总的来说,Reactor和Proactor模式都是基于事件分发的网络编程模式,区别在于Reactor模式是基于“待完成”的I/O事件,而Proactor模式则是基于“已完成”的I/O事件。因此,Proactor模式更加高效,因为它避免了线程间的协作,可以更快地响应I/O事件,但是它的实现相对比较复杂。

IO复用

所谓IO复用,目的是解决前述问题中的每个socket开一个线程的问题。

使用IO复用的机制或者进行IO复用设计(如上文的Reactor模式),使用一个进程监听多个socket。

例如使用一个线程监听多个事件源(如socket),当有事件发生或者可读后,通知对应的程序处理。

落实到Linux内核来帮你做,就变成了Linux IO复用机制,以前有select poll。现在是epoll。

epoll 机制与Reactor

epoll

epoll是Linux下的一个I/O多路复用技术,用于高效地处理大量并发连接。它提供了一组接口,用于注册、修改和删除文件描述符的监听事件。

epoll的接口包括以下几个:

  1. epoll_create():创建一个新的epoll实例,并返回一个指向它的文件描述符。
  2. epoll_ctl():注册、修改或删除文件描述符的监听事件。它需要传入epoll实例的文件描述符、文件描述符、事件类型和回调函数。
  3. epoll_wait():等待注册的文件描述符就绪,并返回就绪的文件描述符列表。它需要传入epoll实例的文件描述符和最大等待时间。
  4. epoll_pwait():与epoll_wait类似,但是可以设置超时时间,并且可以同时处理多个事件。

使用epoll的一般步骤如下:

  1. 创建一个epoll实例,获取其文件描述符。
  2. 使用epoll_ctl()函数注册需要监听的事件和回调函数。
  3. 在需要处理事件的时候,调用epoll_wait()或epoll_pwait()函数等待事件的发生。
  4. 处理就绪的事件。

Reactor

Reactor模式是一种事件驱动的设计模式,用于处理高并发环境下的I/O多路复用问题。它由以下几个组件组成:

  1. Reactor(反应器):是一个接口,定义了注册事件处理器、分发事件和事件处理的方法。通常,Reactor通过异步方式将事件分发给注册的事件处理器。
  2. Event Handler(事件处理器):是一个接口,定义了事件的回调方法。事件处理器实现了Reactor接口,并注册自己感兴趣的事件类型。当事件发生时,Reactor调用事件处理器的回调方法来处理事件。
  3. Concrete Event Handler(具体事件处理器):是事件处理器的实现。在其内部实现了事件处理器的回调方法,进行业务逻辑处理。
  4. Initiation Dispatcher(初始分发器):实际上就是Reactor角色。它本身定义了一些规范,这些规范用于控制事件的调度方式。同时又提供了应用进行事件处理器的注册、删除等操作。它本身是整个事件处理器的核心所在,Initiation Dispatcher会通过Synchronous Event Demultiplexer来等待事件的发生。

工作方式:

  1. 应用程序创建Reactor对象,并注册感兴趣的事件类型和对应的事件处理器。
  2. 当事件发生时,Initiation Dispatcher通过Synchronous Event Demultiplexer将事件传递给对应的事件处理器。
  3. 事件处理器接收到事件后,执行相应的业务逻辑处理。
  4. 处理完成后,事件处理器会再次注册自己感兴趣的事件类型,以便后续继续处理事件。

可以看出,epoll是一个近似于reactor的实现,或者说,基于epoll机制,可以比较方便的实现一个reactor模式来实现事件驱动程序设计。

我们需要做的是,在epoll 的wait返回后,根据返回的socket 就绪列表,去分发,去调用对应的处理程序。

相较于异步IO来说,在分发的时候这个socket还没有被读取,需要应用程序去读取,但是在调用读取的系统调用的时候,也会存在一个阻塞读取数据,例如从内核态拷贝到用户态的过程,我觉得这对目前的工作来讲已经过于玄学了,车端的应用没互联网那么夸张。

Proactor模式 异步IO Boost.asio 协程

Proactor模式是一种消息异步通知的设计模式,它主要用于处理高并发环境下的I/O多路复用问题。以下是Proactor模式的各个组件及其功能和工作方式:

  1. Handle句柄:用于标识socket连接或者是打开文件。在网络服务器中,每个客户连接都会创建不同的套接字句柄,当异步连接、读、写操作执行完成时,完成事件会出现在这些句柄上。
  2. Asynchronous Operation Processor(异步操作处理器):负责执行异步操作,一般由操作系统内核实现。
  3. Asynchronous Operation(异步操作):这是应用程序发出的服务请求,比如异步的通过套接字句柄读写数据。当异步操作激活后,操作不需要借用回调线程的控制即可执行。因此从回调者角度看,操作的执行是异步的。
  4. Completion Event Queue(完成事件队列):异步操作完成的结果会放到队列中,等待后续使用。
  5. Proactor(主动器):为应用程序提供事件循环,从完成事件队列中取出异步操作的结果,分别调用相应的后续处理逻辑。
  6. Completion Handler(完成事件接口):一般是由回调函数组成的接口。
  7. Concrete Completion Handler(完成事件处理逻辑):完成接口定义特定的应用处理逻辑。

在业务流程及时序图中,应用程序启动后,会调用异步处理器提供的异步操作接口来发起异步操作。异步操作处理器接收到操作请求后,会执行相应的异步操作。当操作完成后,会将完成事件放入完成事件队列中。Proactor会不断地轮询完成事件队列,一旦发现有完成事件,就会调用相应的回调函数来处理完成事件。

Boost.Asio是一个广泛使用的C++库,用于处理低级网络编程和并发任务。它实现了Proactor模式,提供了一种高效和灵活的方式处理异步I/O操作。

在Boost.Asio中,Proactor模式的应用主要体现在异步I/O操作的处理上。具体来说,当应用程序发起一个异步操作(如异步读或写)时,Boost.Asio会将其封装为一个异步操作对象,并将其注册到异步事件处理器中。

异步事件处理器使用异步事件分发器(Asynchronous Event Demultiplexer)来等待事件完成。当异步操作完成时,完成事件会被放入完成事件队列中。然后,Proactor会调用异步事件分发器,将完成事件返回给其调用者。

在处理完成事件时,Proactor会调用相应的回调函数。

这个回调函数通常是由应用程序通过boost::bind创建的函数对象,用于处理异步操作的结果。

通过这种方式,Boost.Asio实现了高效的异步I/O处理,使得应用程序可以在不阻塞主线程的情况下处理大量的并发连接和请求。同时,Proactor模式还提供了回调函数的方式,使得应用程序可以灵活地处理完成事件,从而实现了异步编程。

据说,底层是epoll。。

异步IO与协程

之前我想过一个问题,异步IO可以在调用IO操作之后先干别的,不用等待。

在同步(按照时间顺序,典型的C编程)方式下,我调用IO,肯定是要IO的数据,如果不等着拿数据,这个时候能干嘛呢。

直到我看到了协程这个东西,豁然开朗(之前也困惑或协程这个在一个进程内跳来跳去执行的东西有什么用)。

协程和异步IO结合,可以进一步优化IO密集任务。

异步IO,实际上就是给各个事件注册处理函数,让程序在各个处理函数之间跳来跳去,就是纯纯用户态的事情了,甚至可以躲在用户进程里不出来,根本没有切换进程开销,岂不美哉。

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

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

相关文章

如何在uni-app项目中使用路由

在uni-app项目中使用路由代码,需要以下几个步骤: 在uni-app项目的pages.json文件中配置路由信息。在pages数组中添加需要路由管理的页面,例如: "pages": [{"path": "pages/index/index","na…

DeskPins | 将窗口钉在面前

前言 DeskPins | 将窗口钉在面前 有的人,一边看番,一边却在刷题; 有的人,一边网课,一边却在摸鱼。 有的人,一边某xuexi通上考试,一边。。 众所周知,窗口置顶是很常见的一个需求&…

智慧零售技术探秘:关键技术与开源资源,助力智能化零售革新

智慧零售是一种基于先进技术的零售业态,通过整合物联网、大数据分析、人工智能等技术,实现零售过程的智能化管理并提升消费者体验。 实现智慧零售的关键技术包括商品的自动识别与分类、商品的自动结算等等。 为了实现商品的自动识别与分类,…

vue3+ts 代理的使用

简单封装request.ts import axios from "axios";// 1.创建axios对象 const serviceaxios.create();// 2.请求拦截器 service.interceptors.request.use(config>{return config; },error>{Promise.reject(error); })// 3.响应拦截器 service.interceptors…

走进数字金融峰会,为金融科技数字化赋能

12月20—21日,FSIDigital数字金融峰会在上海圆满召开。本次峰会包含InsurDigital数字保险峰会和B&SDigital数字银行与证券峰会2场平行峰会;吸引了近600位来自保险、银行、证券以及金融科技等行业的领导者和专家齐聚一堂,共同探讨金融业数…

docker 私有仓库

Docker 私有仓库 一、私有仓库搭建 # 1、拉取私有仓库镜像 docker pull registry # 2、启动私有仓库容器 docker run -id --nameregistry -p 5000:5000 registry # 3、打开浏览器 输入地址http://私有仓库服务器ip:5000/v2/_catalog,看到{"repositories&quo…

瑞熙贝通助力实验室原始记录无纸化管理

一、系统概述 随着互联网时代的发展,实验室管理信息化功能日趋完善,涉及维度也不断扩大,实现实验室原始记录的电子化和数字化,提高实验室工作质量和效率,是实验室发展大势所趋。在实验室信息管理系统得到日益广泛应用…

云计算:现代技术的基本要素

众所周知,在儿童教育的早期阶段,幼儿园都会传授塑造未来行为的一些基本准则。 今天,我们可以以类似的方式思考云计算:它已成为现代技术架构中的基本元素。云现在在数字交互、安全和基础设施开发中发挥着关键作用。云不仅仅是另一…

nodejs+vue+微信小程序+python+PHP的热带野生动物园景点预约订票系统的设计与实现-计算机毕业设计推荐

管理员是系统的管理者,拥有系统的所有权限,通过系统设定的账号和密码登录后对系统进行管理,包括密码修改、用户管理。新闻公告的管理、景点管理、订单管理。管理员登录中,通过用户的登录名和密码到热带野生动物园景点预约订票系统…

【数据结构】LRU缓存的简单模拟实现(leetcode力扣146LRU缓存)

文章目录 一、定义二、LRU模拟实现二、代码实现 一、定义 LRU是Least Recently Used的缩写,意思是最近最少使用,它是一种Cache替换算法。 Cache的容量有限,因此当Cache的容量用完后,而又有新的内容需要添加进来时, 就…

几种串口扩展电路

一、IIC串口扩展电路 LCT200 是一款可以通过 I2C 接口通讯,拓展 2 路独立串口的通讯芯片,同时也支持通过 2 路串口读写 I2C 接口的数据。LCT200 的封装为 TSSOP-20。 主要功能:⚫ 通过对 I2C 接口读写实现拓展 2 路独立串口功能 ⚫ 通过读写…

【Python3】\u字符与中文字串互转

小水。 encode和decode: str没有decode函数,但对应的有encode函数,该函数作用是转码为bytes对象bytes通过decode函数转换回对应的str对于一些偏激的(可以用过分来形容)的字符串,例如一二三\\u56db\\u4e94\\u516d,是有…

小狐狸GPT付费2.4.9 去除授权弹窗版

后台安装步骤: 1、在宝塔新建个站点,php版本使用7.2 、 7.3 或 7.4,把压缩包上传到站点根目录,运行目录设置为/public 2、导入数据库文件,数据库文件是 /db.sql 3、修改数据库连接配置,配置文件是/.env 4、…

时间Date

你有没有思考过时间问题: 前端为什么可以直接看见时间格式的数据 后端怎么接受的数据,怎么处理的 一般来说:前端传输来数据都是时间格式的字符串,那么后端需要能够解析时间格式的字符串,归功于JSONFormat ,可以解析…

YOLOv8改进 | 主干篇 | RevColV1可逆列网络(特征解耦助力小目标检测)

一、本文介绍 本文给大家带来的是主干网络RevColV1,翻译过来就是可逆列网络去发表于ICLR2022,其是一种新型的神经网络设计(和以前的网络结构的传播方式不太一样),由多个子网络(列)通过多级可逆连接组成。这种设计允许…

【Python_09】Python基础语法(数据容器之列表详解)

文章目录 概述Python中的数据容器列表1.1 定义列表1.2列表取值与修改1.3 列表遍历1.3.1 使用for循环遍历列表1.3.2 使用while循环遍历1.3.3 使用enumerate()函数获取索引和元素 1.4 列表常用方法 概述 数据容器是用来存储和组织数据的数据结构和对象。可以以不同的方式存储和操…

SpringBoot Event,事件驱动轻松实现业务解耦

什么是事件驱动 Spring 官方文档AWS Event Driven 简单来说事件驱动是一种行为型设计模式,通过建立一对多的依赖关系,使得当一个对象的状态发生变化时,所有依赖它的对象都能自动接收通知并更新。即将自身耦合的行为进行拆分,使拆…

@click 默认传递原生的事件对象

项目场景 [Day1] <template><div id"app"><h1>小黑记事本</h1><button click"handleClick">www</button><div class"head"><!-- 按键&#xff08;回车&#xff09;按下&#xff0c;出发add事件&…

Leetcode---376周赛---中位数贪心

题目列表 2965. 找出缺失和重复的数字 2966. 划分数组并满足最大差限制 2967. 使数组成为等数数组的最小代价 2968. 执行操作使频率分数最大 一、找到缺失和重复的数字 由于数据范围不是很大&#xff0c;可以直接暴力统计每个数字出现的次数&#xff0c;时间复杂度为O(n^2…

【Java中序列化的原理是什么(解析)】

&#x1f341;序列化的原理是什么&#xff1f; &#x1f341;典型-----解析&#x1f341;拓展知识仓&#x1f341;Serializable 和 Externalizable 接门有何不同? &#x1f341;如果序列化后的文件或者原始类被篡改&#xff0c;还能被反序列化吗?&#x1f341;serialVersionU…