1.8.C++项目:仿muduo库实现并发服务器之eventloop模块的设计

news2024/11/26 15:41:49

项目完整在:

文章目录

  • 一、eventloop模块:进行事件监控,以及事件处理的模块
  • 二、提供的功能
  • 三、实现思想
    • (一)功能
    • (二)意义
    • (三)功能设计
  • 四、框架
  • 五、代码

一、eventloop模块:进行事件监控,以及事件处理的模块

在这里插入图片描述

  • 进行事件监控管理的模块
  • 这个模块就是我们所说的One thread one loop 中的loop,也就是我们所说的Reactor
  • 这个模块必定是一个模块对于一个线程

二、提供的功能

这个模块和线程是一一对应的!
监听了一个链接,如果这个连接一旦就绪,就要进行事件处理!
但是如果这个描述符,在多个线程中都触发了了事件,进行处理,就会存在线程安全问题!
因此我们需要将一个链接的事件监控, 以及连接事件处理,以及其他操作都放在同一个线程中!
如何保证一个连接的所有操作都在eventloop对应的线程中!
给eventLOOP模块中,都添加一个任务队列!
对连接的所有操作,都进行一次封装,将对连接的操作当作任务都添加到任务队列中!

三、实现思想

(一)功能

  1. 在线程中对描述符进行事件监控!
  2. 有描述符就绪则对描述符进行事件处理,(如何保证处理回调函数中的操作都在线程中)
  3. 所有的就绪事件处理完了,这时候再去将任务队列中的所有任务一一执行! 这样能够保证对于所有链接的所有操作,都是在一个线程中进行的,不涉及线程安全问题!
    但是对于任务队列中的操作有线程安全的问题,只需要给task的操作架一把锁即可!

(二)意义

对于服务器的所有事件都是由EventLoop模块来完成
每一个Connection连接,都会绑定一个EventLoop模块和线程,因为外界对于连接的所有操作,都要放到同一个线程中进行!

(三)功能设计

  1. 事件监控
    使用Poller模块
    有事件就绪则进行事件处理!
  2. 执行任务队列中的任务!
    注意点:
    因为有可能因为等待描述符IO事件就绪,执行流流程阻塞,这个时候任务对立中的任务得不到执行!
    因此得有一个事件通知的东西,能够唤醒事件监控的阻塞!
    当事件就绪,需要处理的时候,处理过程中,如果对连接要进行某些操作!
    这些操作必须要在Eventloop对应的线程中进行,保证对连接的各项操作都是线程安全的。
  3. 如果执行的操作就在本线程中,不需要将操作压入队列了,可以直接执行!
  4. 如果执行的操作不在线程中,才需要加入任务池,等到事件处理完了之后就行执行任务!

四、框架

 class Eventloop {
private:
        std::thread::id _thread_id; // 线程ID
        int _event_fd // eventfd 唤醒IO事件监控有可能的阻塞!!!
        Poller _poller; // 进行所有描述符的事件监控
        using Functor = std::function<void()>;
        std::vector<Functor> _task; // 任务池
        std::mutex _mutex; // 实现任务池操作的线程安全!!!
public:
        void runAllTask();
public:
        Eventloop();
        void runInLoop(const Functor&cb); // 判断将要执行的任务是否处于当前线程中,如果是则执行,不是则压入队列。
        void queueInLoop(const Functor&cb);  // 将操作压入任务池!
        bool isInLoop(); //永远判断当前线程是否是EventLoop所对应的线程
        void updateEvent(Channel* channel); // 添加/修改描述符的事件监控
        void removeEvent(Channel* channel);  // 移除描述符的监控
        void Start(); // 任务监控完毕进行处理任务! 三步走:事件监控-》就绪事件处理-》执行任务

};

五、代码


class EventLoop {
private:
        using Functor = std::function<void()>;
        std::thread::id _thread_id; // 线程ID
        int _event_fd; // eventfd 唤醒IO事件监控有可能的阻塞!!!
        std::unique_ptr<Channel> _event_channel; 
         Poller _poller;//进行所有描述符的事件监控
        
        std::vector<Functor> _tasks; // 任务池
        std::mutex _mutex; // 实现任务池操作的线程安全!!!
        TimerWheel _timer_wheel;//定时器模块
public: 
        // 执行任务池中的所有任务!!
        void runAllTask() {
                std::vector<Functor> functor; {
                        std::unique_lock<std::mutex> _lock(_mutex); // 出了作用域,锁就会被解开!!
                        _tasks.swap(functor);
                }
                for (auto &f : functor) {
                        f();
                }
                return ;
        }
        static int createEventFd() {
                int efd = eventfd(0,EFD_CLOEXEC | EFD_NONBLOCK);
                if (efd < 0) {
                        ERR_LOG("CREATE ENVENTED FAILED !!!");
                        abort();
                }
                return efd;
        }
        void readEventfd() {
                uint64_t res = 0;
                int ret = read(_event_fd,&res,sizeof(res));
                if (ret < 0) {
                        if (errno == EINTR || errno == EAGAIN) {
                        return;
                }
                        ERR_LOG("READ EVENTFD FAILED!");
                        abort();
                }
                return ;
        }
        void weakEventFd() {
                uint64_t val = 1;
                int ret = write(_event_fd,&val,sizeof(val));
                if (ret < 0) {
                if (errno == EINTR) {
                    return;
                }
                ERR_LOG("READ EVENTFD FAILED!");
                abort();
            }
            return ;

        }
public:
        EventLoop():_thread_id(std::this_thread::get_id()), 
                    _event_fd(createEventFd()), 
                    _event_channel(new Channel(this, _event_fd)),
                    _timer_wheel(this) {
            //给eventfd添加可读事件回调函数,读取eventfd事件通知次数
            _event_channel->setReadCallback(std::bind(&EventLoop::readEventfd, this));
            //启动eventfd的读事件监控
            _event_channel->enableRead();
        }
        void runInLoop(const Functor&cb) { // 判断将要执行的任务是否处于当前线程中,如果是则执行,不是则压入队列。
                if (isInLoop()) {
                        return cb();
                }
        }
        void queueInLoop(const Functor&cb) { // 将操作压入任务池!
                std::unique_lock<std::mutex> _lock(_mutex);
                //唤醒有可能因为没有事件就绪,而导致的epoll阻塞;
                //其实就是给eventfd写入一个数据,eventfd就会触发可读事件
                _tasks.push_back(cb);
                weakEventFd();
        }
        bool isInLoop() { //永远判断当前线程是否是EventLoop所对应的线程
                return (_thread_id == std::this_thread::get_id());
        }
        void updateEvent(Channel* channel) {// 添加/修改描述符的事件监控
                return _poller.UpdateEvent(channel); 
        }
        void removeEvent(Channel* channel) {   // 移除描述符的监控
                return _poller.removeEvent(channel);
        }
        void TimerAdd(uint64_t id, uint32_t delay, const TaskFunc &cb) { return _timer_wheel.TimerAdd(id, delay, cb); }
        void TimerRefresh(uint64_t id) { return _timer_wheel.TimerRefresh(id); }
        void TimerCancel(uint64_t id) { return _timer_wheel.TimerCancel(id); }
        bool HasTimer(uint64_t id) { return _timer_wheel.HasTimer(id); }
        void Start() { // 任务监控完毕进行处理任务! 
                // 三步走:事件监控-》就绪事件处理-》执行任务
                std::vector<Channel*> actives;
                _poller.Poll(&actives);

                for (auto &channel : actives) {
                        channel -> handleEvent();
                }
                runAllTask();
        }

};

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

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

相关文章

Andriod 简单控件

目录 一、文本显示1.1 设置文本内容1.2 设置文本大小1.3 设置文本颜色 二、视图基础2.1 设置视图宽高2.2 设置视图间距2.3 设置视图对齐方式 三、常用布局3.1 线性布局LinearLayout3.2 相对布局RelativeLayout3.3 网格布局GridLayout3.4 滚动视图ScrollView 四、按钮触控4.1 按…

Acwing 905. 区间选点

Acwing 905. 区间选点 知识点题目描述思路讲解代码展示 知识点 贪心 题目描述 思路讲解 代码展示 #include <iostream> #include <algorithm>using namespace std;const int N 1e5 10;int n;struct Range {int l, r;bool operator<(const Range &W) co…

宽带光纤接入网中影响家宽业务质量的常见原因有哪些

1 引言 虽然家宽业务质量问题约60%发生在家庭网&#xff08;见《家宽用户家庭网的主要质量问题是什么&#xff1f;原因有哪些》一文&#xff09;&#xff0c;但在用户的眼里&#xff0c;所有家宽业务质量问题都是由运营商的网络质量导致的&#xff0c;用户也因此对不同运营商家…

红外遥控器 数据格式,按下及松开判断

红外遥控是一种无线、非接触控制技术&#xff0c;具有抗干扰能力强&#xff0c;信息传输可靠&#xff0c;功耗低&#xff0c;成本低&#xff0c;易实现等显著优点&#xff0c;被诸多电子设备特别是家用电器广泛采用&#xff0c;并越来越多的应用到计算机系统中。 同类产品的红…

国庆作业5

物理层&#xff1a;负责传输比特流。它将数据转换为比特流并传输。 数据链路层&#xff1a;相邻节点的可靠传输。 网络层&#xff1a;负责在不同的网络中进行数据包的路由和转发。。 传输层&#xff1a;提供端到端的连接。 会话层&#xff1a;负责建立、管理和终止会话。它…

如何用ChatGPT学或教英文?5个使用ChatGPT的应用场景!

原文&#xff1a;百度安全验证 AI工具ChatGPT的出现大幅改变许多领域的运作方式&#xff0c;就连「学英文」也不例外&#xff01;我发现ChatGPT应用在英语的学习与教学上非常有意思。 究竟ChatGPT如何改变英文学习者(学生)与教学者(老师)呢&#xff1f; 有5个应用场景我感到…

idea清空缓存类

解决办法 网上有很多是让你去清空什么maven依赖&#xff0c;但假如这个项目是你不可以大刀阔斧的话 可以清空idea缓存 选择 Invalidate 开头的 然后全选 运行重启idea OK

免费 AI 代码生成器 Amazon CodeWhisperer 初体验

文章作者&#xff1a;浪里行舟 简介 随着 ChatGPT 的到来&#xff0c;不由让很多程序员感到恐慌。虽然我们阻止不了 AI 时代到来&#xff0c;但是我们可以跟随 AI 的脚步&#xff0c;近期我发现了一个神仙 AI 代码生产工具 CodeWhisperer &#xff0c;它是一项基于机器学习的服…

从其它环境转移到Nacos的方法-NacosSync

理解 NacosSync 组件启动 NacosSync 服务通过一个简单的例子&#xff0c;演示如何将注册到 Zookeeper 的 Dubbo 客户端迁移到 Nacos。 介绍 NacosSync是一个支持多种注册中心的同步组件,基于Spring boot开发框架,数据层采用Spring Data JPA,遵循了标准的JPA访问规范,支持多种…

【Golang】数组 切片

【Golang】数组 && 切片 1、数组 基本概念 数组是一个由固定长度的特定类型元素组成的序列&#xff0c;一个数组可以由零个或多个元素组成 因为数组的长度是固定的&#xff0c;所以在Go语言中很少直接使用数组 数组初始化 //1、默认数组中的值是类型的默认值 var arr…

buuctf-[RoarCTF 2019]Easy Java

第一次遇到java类的题目 打开环境&#xff0c;很像sql 点击help 以为是文件包含&#xff0c;&#xff0c;但是不对 这里需要了解JAVA WEB目录结构 WEB-INF&#xff1a;Java的web应用安全目录&#xff1b; 此外如果想在页面访问WEB-INF应用里面的文件&#xff0c;必须要通过w…

localStorage实现历史记录搜索功能

&#x1f4dd;个人主页&#xff1a;爱吃炫迈 &#x1f48c;系列专栏&#xff1a;JavaScript &#x1f9d1;‍&#x1f4bb;座右铭&#xff1a;道阻且长&#xff0c;行则将至&#x1f497; 文章目录 为什么使用localStorage如何使用localStorage实现历史记录搜索功能&#xff08…

矢量图形编辑软件illustrator 2023 mac特点介绍

illustrator 2023 mac是一款矢量图形编辑软件&#xff0c;用于创建和编辑排版、图标、标志、插图和其他类型的矢量图形。 illustrator mac软件特点 矢量图形&#xff1a;illustrator创建的图形是矢量图形&#xff0c;可以无限放大而不失真&#xff0c;这与像素图形编辑软件&am…

[Linux]线程互斥

[Linux]线程互斥 文章目录 [Linux]线程互斥线程并发访问问题线程互斥控制--加锁pthread_mutex_init函数pthread_mutex_destroy函数pthread_mutex_lock函数pthread_mutex_unlock函数锁相关函数使用示例使用锁的细节加锁解锁的实现原理 线程安全概念常见的线程不安全的情况常见的…

蓝桥杯每日一题2023.10.2

时间显示 - 蓝桥云课 (lanqiao.cn) 题目描述 题目分析 输入为毫秒&#xff0c;故我们可以先将毫秒转化为秒&#xff0c;由于只需要输出时分&#xff0c;我们只需要将天数去除即可&#xff0c;可以在这里多训练一次天数判断 #include<bits/stdc.h> using namespace std…

检索qpython文件夹下.py

需求口令 检索 /storage/emulated/0/qpython 文件夹下的.py文件 编号原文件名&#xff1a;复制到/storage/emulated/0/qpython/py文件/ 没有文件夹就创建 检索qpython文件夹下.py&#xff0c;复制到py文件单独路径 根据这个提问清单和步骤&#xff0c;我们需要完成以下任务&…

Audio2Face的工作原理

预加载一个3D数字人物模型(Digital Mark),该模型可以通过音频驱动进行面部动画。 用户上传音频文件作为输入。 将音频输入馈送到预训练的深度神经网络中。 Audio2Face加载预制的3d人头mesh 3D数字人物面部模型由大量顶点组成,每个顶点都有xyz坐标。 深度神经网络输入音频特征,…

C++基础语法(继承)

终于&#xff0c;经过一路的过关斩将&#xff0c;我们来到了继承面前。还记得在最初学习类于对象时&#xff0c;那个对封装概念一直模糊不清的自己&#xff0c;还记得被模板&#xff0c;被迭代器折磨的日日夜夜吗&#xff1f;这一路你挺过来了&#xff0c;你失去了一些东西&…

zkLogin构建者的最佳实践和业务思考

随着zkLogin在Sui主网上线&#xff0c;构建者可以开始为其应用程序提供丝滑的帐户创建服务。与任何新技术集成一样&#xff0c;构建者需要考虑许多重要的问题&#xff0c;以降低风险并成功优化。 本文概述了其中一些考虑因素&#xff0c;并突出了zkLogin文档中提供的明确指导。…

Linux-centos系统安装MySql5.7

1.配置yum仓库 1.1配置yum仓库 rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 1.2 安装Mysql yum库 rpm -Uvh http://repo.mysql.com//mysql57-community-release-el7-7.noarch.rpm 2.使用yum安装Msql 说明&#xff1a;下载大约5分钟左右 yum -y install mysq…