基于多反应堆的高并发服务器【C/C++/Reactor】(中)添加 删除 修改 释放

在上篇文章(处理任务队列中的任务)中我们讲解了处理任务队列中的任务的具体流程,eventLoopProcessTask函数的作用:

  • 处理队列中的任务,需要遍历链表并根据type进行对应处理,也就是处理dispatcher中的任务。
// 处理任务队列中的任务
int eventLoopProcessTask(struct EventLoop* evLoop) {
    ...
    while (head!=NULL) {
        struct Channel* channel = head->channel;
        if(head->type == ADD) {
            // 添加
            eventLoopAdd(evLoop,channel);
        }
        else if(head->type == DELETE) {
            // 删除
            eventLoopRemove(evLoop,channel);
        }
        else if(head->type == MODIFY) {
            // 修改
            eventLoopModify(evLoop,channel);
        }
        ...
    }
    ...
    return 0;
}
  • 处理dispatcher中的任务 -- 添加 删除 修改
// 处理dispatcher中的任务
int eventLoopAdd(struct EventLoop* evLoop,struct Channel* channel);
int eventLoopRemove(struct EventLoop* evLoop,struct Channel* channel);
int eventLoopModify(struct EventLoop* evLoop,struct Channel* channel);

 一、添加fdDispatcher的文件描述符检测集合中

把文件描述符fdchannel的对应关系存储到channelMap。这么做是为了什么?

  • dispatcher里边,还有dispatch函数指针,也就是dispatcher->dispatch(evLoop,timeout)。这个是一个检测函数,通过调用dipatch函数,就可以得到激活的文件描述符,得到了激活的文件描述符之后,需要通过这个文件描述符找到它所对应的channel
channelMap->list[fd] = channel; 

把文件描述符添加到dispatcher对应的文件描述符检测集合中

  • 首先从evLoop里边把dispatcher这个实例给取出来:evLoop->dispatcher,在dispatcher里边有一系列的函数指针,其中有一个叫做add。这个add就是把文件描述符添加到dispatcher对应的文件描述符检测集合中,函数指针add,指向的底层函数可能是不一样的,这个取决于我们选择的dispatcher模型,它有可能是poll,有可能是epoll,也有可能是select。选择的IO模型不一样,add这个函数指针指向的函数的处理动作也就不一样
evLoop->dispatcher->add(channel,evLoop); 

 (1)eventLoopAdd 

// 将任务队列中的任务添加到Dispatcher的文件描述符检测集合中
int eventLoopAdd(struct EventLoop* evLoop,struct Channel* channel) {
    int fd = channel->fd;// 取出文件描述符fd
    struct ChannelMap* channelMap = evLoop->channelMap;// channelMap存储着channel和fd之间的对应关系
    // 需要判断channelMap里边是否有fd 和 channel对应的键值对(其中,文件描述符fd对应的就是数组的下标)
    if(fd >= channelMap->size) {
        // 没有足够的空间存储键值对 fd->channel ==> 扩容
        if(!makeMapRoom(channelMap,fd,sizeof(struct Channel*))) {
            return -1;
        }
    }
    // 找到fd对应的数组元素位置,并存储
    if(channelMap->list[fd] == NULL) {
        channelMap->list[fd] = channel;
        evLoop->dispatcher->add(channel,evLoop);
    } 
    return 0;
}

1. 主题: 文件描述符与ChannelDispatcher中的交互

2. 重要信息:

  • Channel结构体:封装了文件描述符,并在其中存储了所需的数据
  • 添加文件描述符到Dispatcher:当有新文件描述符需要添加时,它会被放入Dispatcher的检测集合中
  • 文件描述符激活与处理:如果某个文件描述符在Dispatcher的检测集合中被激活,可以通过调用dispatch函数获取这个文件描述符,并进一步找到对应的Channel
  • 回调函数与事件处理:一旦找到对应的Channel,可以根据触发的事件调用在Channel结构体中注册的回调函数
  • ChannelMap结构体:用于存储文件描述符Channel之间的对应关系。它是一个数组,下标(即数组索引)对应文件描述符的值
  • 扩容ChannelMap:如果当前ChannelMap的容量不足,可以通过调用 makeMapRoom 函数进行扩容

3. 总结:详细介绍了如何通过Channel结构体和Dispatcher处理文件描述符,以及如何通过回调函数处理不同的事件。同时,还提到了如何使用和扩容ChannelMap结构体来存储文件描述符与Channel之间的对应关系

4. 思考与理解: 深入理解文件描述符、ChannelDispatcher之间的关系及其工作原理。 在实际编程中,考虑如何有效地使用和扩展ChannelMap结构体,以适应更多的文件描述符和事件处理需求。 考虑如何在代码中实现更清晰的事件处理逻辑,以提高代码的可读性和可维护性

总结:主要描述了如何通过文件描述符在channel中添加、激活和触发事件。其中,channelMap结构体用于存储文件描述符和channel的对应关系,而EventLoop结构体则用于存储channel和文件描述符的对应关系

核心观点 :

  • 文件描述符与channel的对应关系存储在channelMap中,通过数组索引对应文件描述符的值
  • 如果channelMap容量不足,需要调用函数进行扩容

二、从Dispatcher的文件描述符检测集合中删除fd

把任务队列里面的节点从dispatcher的检测集合中删除,调用dispatcher里边的remove函数

  • 如果我们要删除的这个文件描述符并不在channelMap中存储着,说明我们要操作的这个文件描述符并不在dispatcher的检核集合中。因为它在检测集合里边,在添加的时候就会把文件描述符fdchannel的映射关系也存储到channelMap里边去了。故只要它在检测集合里边,它肯定就在channelMap里边。如果它不在channelMap里边,那么它就肯定不在检测集合里边。如果它不在检测集合里边,就无需做任何事情,直接返回-1
  • 如果文件描述符fd在检测集合里,就从中把它删除

int eventLoopRemove(struct EventLoop* evLoop,struct Channel* channel) {
    int fd = channel->fd;
    struct ChannelMap* channelMap = evLoop->channelMap;
    if(fd >= channelMap->size) {
        return -1;
    }
    int ret = evLoop->dispatcher->remove(channel,evLoop);
    return ret;
}

三、修改Dispatcher的检测集合里边文件描述符事件的函数

当我们需要修改检测集合中的某个文件描述符事件时,首先需要判断该文件描述符是否在channelMap中。如果文件描述符存在,我们可以根据它获取一个非零的地址,这个地址实际上是channel实例的地址

  • 如果获取的地址为,说明传入的文件描述符对应的channel有问题;
  • 如果不为空,我们可以通过evLoop实例调用其函数指针modify

int eventLoopModify(struct EventLoop* evLoop,struct Channel* channel) {
    int fd = channel->fd;
    struct ChannelMap* channelMap = evLoop->channelMap;
    if(fd >= channelMap->size || channelMap->list[fd] == NULL) {
        return -1;
    }
    int ret = evLoop->dispatcher->modify(channel,evLoop);
    return ret;
}

四、释放channel

// 释放channel
int destroyChannel(struct EventLoop* evLoop,struct Channel* channel);

从检测集合中删除文件描述符后,对应的channel实例没有用了,因此需要释放它。在释放channel之前,我们需要关闭文件描述符,因为不再需要检测它的事件。此外,由于channel本身是一个指向堆内存的指针,我们需要释放这块堆内存。 

// 释放channel
int destroyChannel(struct EventLoop* evLoop,struct Channel* channel) {
    // 删除 channel 和 fd 的对应关系
    evLoop->channelMap->list[channel->fd] = NULL;
    // 关闭 fd
    close(channel->fd);
    // 释放 channel 内存
    free(channel);
    return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux之Ubuntu环境Jenkins部署前端项目

今天分享Ubuntu环境Jenkins部署前端vue项目 一、插件安装 1、前端项目依赖nodejs,需要安装相关插件 点击插件管理,输入node模糊查询 选择NodeJS安装 安装成功 2、配置nodejs 点击后进入 点击新增 NodeJS 配置脚手架类型:如果不填 默认npm …

华为HarmonyOS 创建第一个鸿蒙应用 运行Hello World

使用DevEco Studio创建第一个项目 Hello World 1.创建项目 创建第一个项目,命名为HelloWorld,点击Finish 选择Empty Ability模板,点击Next Hello World 项目已经成功创建,接来下看看效果 2.预览 Hello World 点击右侧的预…

[VUE]2-vue的基本使用

目录 vue基本使用方式 1、vue 组件 2、文本插值 3、属性绑定 4、事件绑定 5、双向绑定 6、条件渲染 7、axios 8、⭐跨域问题 🍃作者介绍:双非本科大三网络工程专业在读,阿里云专家博主,专注于Java领域学习,擅…

RPC基础知识总结

RPC 是什么? RPC(Remote Procedure Call) 即远程过程调用,通过名字我们就能看出 RPC 关注的是远程调用而非本地调用。 为什么要 RPC ? 因为,两个不同的服务器上的服务提供的方法不在一个内存空间,所以&am…

【UML】第17篇 包图

目录 一、什么是包图 二、包图的作用: 三、应用场景: 四、绘图符号的说明: 五、语法: 六、其他要说的 一、什么是包图 包图(Package Diagram)是一种用于描述系统中包和包之间关系的UML图。包是一种将…

Thonny开发ESP32点灯

简介 ESP32是一款功能强大的低功耗微控制器,由乐鑫(Espressif)公司开发。它集成了Wi-Fi和蓝牙功能,适用于各种物联网应用。Thonny是一款基于Python的开源集成开发环境(IDE),专为MicroPython设计…

【数据分享】2024年我国主要城市地铁站点和线路数据

地铁站点与线路数据是我们经常会用到的一种基础数据。去哪里获取该数据呢? 今天我们就给大家分享一份2024年1月采集的全国有地铁城市的地铁站点与线路数据,数据格式为shp,数据坐标为wgs1984地理坐标。数据中不仅包括地铁,也包括轻…

Java Swing手搓坦克大战遇到的问题和思考

1.游戏中的坐标系颇为复杂 像素坐标系还有行列坐标,都要使用,这之间的互相转化使用也要注意 2.游戏中坦克拐弯的处理,非常重要 由于坦克中心点是要严格对齐到一条网格线,并沿着这条线前进的,如果拐弯不做处理&#…

法线变换矩阵的推导

背景 在冯氏光照模型中,其中的漫反射项需要我们对法向量和光线做点乘计算。 从顶点着色器中读入的法向量数据处于模型空间,我们需要将法向量转换到世界空间,然后在世界空间中让法向量和光线做运算。这里便有一个问题,如何将法线…

AI爆文变现:怼量也有技巧!如何提升你的创作收益

做AI爆文项目,赚小钱是没有问题的。 想要赚大钱,就是要做矩阵,怼量。 之前参加训练营的时候,也是要求怼量。 怼量,加高质量文章,让你的收益更高。 如何提升文章质量,减少AI味,AI…

性能优化-OpenMP基础教程(二)

本文主要介绍OpenMP并行编程技术,编程模型、指令和函数的介绍、以及OpenMP实战的几个例子。希望给OpenMP并行编程者提供指导。 🎬个人简介:一个全栈工程师的升级之路! 📋个人专栏:高性能(HPC&am…

动手学深度学习之卷积神经网络之池化层

池化层 卷积层对位置太敏感了,可能一点点变化就会导致输出的变化,这时候就需要池化层了,池化层的主要作用就是缓解卷积层对位置的敏感性 二维最大池化 这里有一个窗口,来滑动,每次我们将窗口中最大的值给拿出来 还是上…

更改ERPNEXT源

更改ERPNEXT源 一, 更改源 针对已经安装了erpnext的,需要更改源的情况: 1, 更改为官方默认源, 进入frapp-bench的目录, 然后执行: bench remote-reset-url frappe //重设frappe的源为官方github地址。 bench remote-reset-url…

ARM工控机Node-red使用教程

嵌入式ARM工控机Node-red安装教程 从前车马很慢书信很远,而现在人们不停探索“科技改变生活”。 智能终端的出现改变了我们的生活方式,钡铼技术嵌入式工控机协助您灵活布建能源管理、大楼自动化、工业自动化、电动车充电站等各种多元性IoT应用&#xff…

IDEA 控制台中文乱码问题解决方法(UTF-8 编码)

设置 IDEA 编码格式 1:打开 IntelliJ IDEA>File>Setting>Editor>File Encodings,将 Global Encoding、Project Encoding、Default encodeing for properties files 这三项都设置成 UTF-8 2:将 vm option 参数改为: -…

spring bean对象request字段自动注入javax.servlet.ServletRequest

##spring bean对象request字段自动注入javax.servlet.ServletRequest 对象里面request全局变量是否线程安全呢? 答案:线程安全 ##代码如下 import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; …

解决“invalid UTF-8 encoding”

有如下一个程序 package mainimport"fmt"func main(){fmt.Println("hello,2024年") }go run xxx.go出现以下的问题 问题“invalid UTF-8 encoding”,无效的utf8编码。有可能是文件的编码不是“utf8” 为了验证猜想,看一下“xxx.go”…

【vue】emit 的理解与使用

文章目录 介绍流程示例效果父组件子组件 介绍 $emit 是 Vue 组件实例中的一个方法,用来触发自定义事件,并向父组件传递信息它接受两个参数:事件名称和可选参数this.$emit(事件名称, 参数);流程 示例 效果 触发前 触发后 父组件 父组件使…

windows安装nvm以及nvm常用命令

目录 1.什么是nvm以及为啥要用nvm 1.什么是nvm 2.为什么要用nvm 2.安装nvm 1. 下载 2. 安装 1.双击解压后的文件,nvm-setup.exe 2.同意 3.安装路径 4.下一步,这里有建议改成自己的文件夹,这个是用来存储通过nvm切换node后版本的存储路径 5.安装…

ssm基于java web 的QQ村旅游网站的设计+vue论文

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