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

项目完整在:

文章目录

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

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

二、提供的功能

这个模块和线程是一一对应的!

监听了一个链接,如果这个连接一旦就绪,就要进行事件处理!

但是如果这个描述符,在多个线程中都触发了了事件,进行处理,就会存在线程安全问题!

因此我们需要将一个链接的事件监控, 以及连接事件处理,以及其他操作都放在同一个线程中!

如何保证一个连接的所有操作都在eventloop对应的线程中!
给eventLOOP模块中,都添加一个任务队列!
对连接的所有操作,都进行一次封装,将对连接的操作当作任务都添加到任务队列中!

三、实现思想

(一)功能

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

(二)意义

对于服务器的所有事件都是由EventLoop模块来完成

每一个Connection连接,都会绑定一个EventLoop模块和线程,因为外界对于连接的所有操作,都要放到同一个线程中进行!

(三)功能设计

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

四、框架

cpp 复制代码
 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(); // 任务监控完毕进行处理任务! 三步走:事件监控-》就绪事件处理-》执行任务

};

五、代码

cpp 复制代码
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();
        }

};
相关推荐
ཌ斌赋ད5 分钟前
FFTW基本概念与安装使用
c++
BUG 40436 分钟前
LINUX--shell
linux·运维·服务器
薄荷故人_38 分钟前
从零开始的C++之旅——红黑树封装map_set
c++
菜鸟小白:长岛icetea42 分钟前
Linux零基础速成篇一(理论+实操)
linux·运维·服务器
悲伤小伞1 小时前
C++_数据结构_详解二叉搜索树
c语言·数据结构·c++·笔记·算法
过过过呀Glik1 小时前
在 Ubuntu 服务器上添加和删除用户
linux·服务器·ubuntu
m0_675988232 小时前
Leetcode3218. 切蛋糕的最小总开销 I
c++·算法·leetcode·职场和发展
Java小白中的菜鸟3 小时前
centos7的磁盘扩容
linux·运维·服务器
黑客老陈4 小时前
面试经验分享 | 北京渗透测试岗位
运维·服务器·经验分享·安全·web安全·面试·职场和发展
橘子师兄4 小时前
如何在自己的云服务器上部署mysql
运维·服务器·mysql