仿muduo库实现高并发服务器--定时器模块的整合

目录

一、前置知识

二、TimeTask

1、成员变量

2、成员函数

三、时间轮TimerWheel

1、成员变量

2、成员函数

四、EventLoop中整合TimerWheel

[五、Channel Poll EventLoop timerwheel整体代码](#五、Channel Poll EventLoop timerwheel整体代码)


一、前置知识

timefd: 实现内核每隔一段时间,给进程一次超时时间(timefd可读)

timewheel: 实现每次执行Runtimetask,都可以执行一波到期任务的定时任务

要实现一个完整的秒级定时器,就需要将这两个功能整合到一起

timerfd设置为每秒钟触发一次定时任务,当事件被触发则运行一次timerwheel的runtimertask,执行以下所有定时任务

timerfd的事件监控与触发,可以融合EventLoop

下面的代码如果看不懂可以看我前面的前置知识详细的讲解的时间轮的思想

二、TimeTask

1、成员变量

定时任务id

定时任务的超时时间

定时任务要执行的任务

删除Timerwheel中保存的定时器对象

是否取消定时任务

2、成员函数

构造

cpp 复制代码
TimerTask(uint64_t id, uint32_t delay, const TaskFunc cd)
        : _id(id), _timeout(delay), _task_cd(cd), _canceled(false)
    {
    }

析构

cpp 复制代码
 ~TimerTask()
    {
        if (_canceled == false)
        {
            _task_cd();
        }

        _release();
    }

设置Release

cpp 复制代码
void SetRelease(const ReleaseFunc &cd)
    {
        _release = cd;
    }

取消定时任务

cpp 复制代码
 void Cancel()
    {
        _canceled = true;
    }

三、时间轮TimerWheel

1、成员变量

秒针(走到哪里哪里就被释放)

表盘最大容量(最大延迟时间)

表盘(vector)

查找表盘的任务(std::unordered_map<uint64_t, WeakTask> _timers;)

管理线程安全(EventLoop *_loop)

设置回调事件(std::unique_ptr<Channel> _timer_channel)

cpp 复制代码
 using WeakTask = std::weak_ptr<TimerTask>;
    using PtrTask = std::shared_ptr<TimerTask>;
    int _tick;     // 秒钟
    int _capacity; // 表盘最大数量
    std::vector<std::vector<PtrTask>> _wheel;
    std::unordered_map<uint64_t, WeakTask> _timers;
    int _timerfd; // 定时器描述符,可读事件回调就是读取计数器,执行任务
    EventLoop *_loop;
    std::unique_ptr<Channel> _timer_channel;

2、成员函数

构造

创建time_fd

_timer_fd的可读事件回调

cpp 复制代码
  void OnTime()
    {

       int times =  ReadTimefd();
       for(int i = 0; i<times; i++)
       {
         RunTimerTask();
       }
       
    }

读取_time_fd

cpp 复制代码
int ReadTimefd()
    {
        uint64_t times;
        int ret = read(_timerfd, &times, sizeof(times));
        if (ret < 0)
        {
            ERR_LOG("READ FAILED");
            abort();
        }
        return ret;
    }

执行表盘中的任务

cpp 复制代码
void RunTimerTask()
    {
        _tick = (_tick + 1) % _capacity;
        _wheel[_tick].clear();
    }

判断任务是否存在

因为定时器任务都涉及到对链接的操作,需要考虑线程安全,所以需要将对_timer_fd的操作压入到EventLoop任务队列中

涉及到对象前后的问题所以在内外进行实现,在那种只写声明

添加任务

cpp 复制代码
 void TimerAddLoop(uint64_t id, uint32_t delay, const TaskFunc &cd)
    {
        PtrTask ptr(new TimerTask(id, delay, cd));
        ptr->SetRelease(std::bind(&TimerWheel::RemoveTimer, this, id));
        _timers[id] = ptr;
        int pos = (_tick + delay) % _capacity;
        _wheel[pos].push_back(ptr);
    }

刷新任务

cpp 复制代码
 void TimerRefreshLoop(uint64_t id)
    {
        auto it = _timers.find(id);
        if (it == _timers.end())
        {
            return;
        }
        PtrTask ptr = it->second.lock();
        int delay = ptr->DelayTime();
        int pos = (delay + _tick) % _capacity;
        _wheel[pos].push_back(ptr);
    }

取消定时任务

cpp 复制代码
  void CancelTaskLoop(uint32_t id)
    {
        auto it = _timers.find(id);
        if (it == _timers.end())
        {
            return;
        }
        PtrTask tr = it->second.lock();
        if (tr)
            tr->Cancel();
    }

四、EventLoop中整合TimerWheel

在成员变量中添加定时器模块

成员函数中添加删除延迟任务

cpp 复制代码
  void TimerAdd(uint64_t id, uint32_t delay, const TaskFunc &cd)
    {
        _time_wheel.TimerAdd(id, delay, cd);
    }
    void TimerRefresh(uint64_t id)
    {
        _time_wheel.TimerRefresh(id);
    }
    void TimerCancel(uint64_t id)
    {
        _time_wheel.TimerCancel(id);
    }
    bool HasTimer(uint64_t id)
    {
        return _time_wheel.HasTimer(id);
    }

五、Channel Poll EventLoop timerwheel整体代码

cpp 复制代码
class Poller;
class EventLoop;
class Channel
{
private:
    uint32_t _events;
    uint32_t _revents;
    int _fd;
    EventLoop *_loop;
    using EventCallBack = std::function<void()>;
    EventCallBack _read_callback;
    EventCallBack _write_callback;
    EventCallBack _error_callback;
    EventCallBack _close_callback;
    EventCallBack _event_callback;

public:
    Channel(EventLoop *loop, int fd) : _fd(fd), _loop(loop), _events(0), _revents(0)
    {
    }
    uint32_t Event()
    {
        return _events;
    }
    int Fd()
    {
        return _fd;
    }
    void SetEvent(uint32_t events)
    {
        _revents = events;
    }
    void SetReadCallback(const EventCallBack &cd)
    {
        _read_callback = cd;
    }
    void SetWriteCallback(const EventCallBack &cd)
    {
        _write_callback = cd;
    }
    void SetErrorCallback(const EventCallBack &cd)
    {
        _error_callback = cd;
    }
    void SetCloseCallback(const EventCallBack &cd)
    {
        _close_callback = cd;
    }
    void SetEventCallback(const EventCallBack &cd)
    {
        _event_callback = cd;
    }
    bool ReadAble() // 是否监控了可读
    {
        return _events & EPOLLIN;
    }
    bool WriteAble()
    {
        return _events & EPOLLOUT;
    }
    void EnableRead() // 启动读事件监控
    {
        _events |= EPOLLIN;
        Update();
    }
    void EnableWrite()
    {
        _events |= EPOLLOUT;
        Update();
    }
    void DisableRead()
    {
        _events &= ~EPOLLIN;
        Update();
    }
    void DisableWrite()
    {
        _events &= ~EPOLLOUT;
        Update();
    }
    void DisableAll()
    {
        _events = 0;
        Update();
    }
    void Remove();
    void Update();
    // 事件处理 一旦链接接触了事件,就调用了这个函数,自己触发什么自己决定
    void HandleEvents()
    {
        if (_revents & EPOLLIN || _revents & EPOLLRDBAND || _revents & EPOLLPRI)
        {
           
            if (_read_callback)
                _read_callback();
        }
        if (_revents & EPOLLOUT)
        {
            if (_event_callback)
                _event_callback();
            if (_write_callback)
                _write_callback();
        }
        else if (_revents & EPOLLERR)
        {
            if (_error_callback)
                _error_callback();
        }
        else if (_revents & EPOLLHUP)
        {
            if (_close_callback)
                _close_callback();
        }
         if (_event_callback)
                _event_callback();
    }
};
#define MAX_EPOLLEVENTS 1024
class Poller
{
private:
    int _epfd;
    std::unordered_map<int, Channel *> _channels;
    struct epoll_event _evs[MAX_EPOLLEVENTS];
    void Update(Channel *channel, int op)
    {
        int fd = channel->Fd();
        struct epoll_event ev;
         memset(&ev, 0, sizeof(ev)); 
        ev.data.fd = fd;
        ev.events = channel->Event(); 
        int ret = epoll_ctl(_epfd, op, fd, &ev);
       
        if (ret < 0)
        {
             ERR_LOG("EPOLLCTL FAILED! errno: %d, %s", errno, strerror(errno));
            abort();
        }
        return;
    }

public:
    Poller()
    {
        _epfd = epoll_create(MAX_EPOLLEVENTS);
        if (_epfd < 0)
        {
            ERR_LOG("EPOLL_CREATE FAILED");
            abort();
        }
    }
    bool HasChannel(Channel *channel)
    {
        auto it = _channels.find(channel->Fd());
        if (it == _channels.end())
        {
            return false;
        }
        return true;
    }
    // 修改添加事件监控
    void UpdateEvent(Channel *channel)
    {
        if (HasChannel(channel) == false)
        {
            Update(channel, EPOLL_CTL_ADD);
            _channels.insert(std::make_pair(channel->Fd(), channel));
            return;
        }
        return Update(channel, EPOLL_CTL_MOD);
    }
    void RemoveEvent(Channel *channel)
    {
        auto it = _channels.find(channel->Fd());
        if (it != _channels.end())
        {
            _channels.erase(it);
        }
        Update(channel, EPOLL_CTL_DEL);
    }
    void Poll(std::vector<Channel *> *active)
    {
        int nfds = epoll_wait(_epfd, _evs, MAX_EPOLLEVENTS, -1);
        if (nfds < 0)
        {
            if (errno == EINTR)
            {
                return;
            }
            ERR_LOG("EPOLL_WAIT FAILED");
            abort();
        }
        for (int i = 0; i < nfds; i++)
        {
            auto it = _channels.find(_evs[i].data.fd);
            assert(it != _channels.end());
            it->second->SetEvent(_evs[i].events);
            active->push_back(it->second);
        }
    }
};
using TaskFunc = std::function<void()>;
using ReleaseFunc = std::function<void()>;
class TimerTask
{
private:
    uint64_t _id;
    uint32_t _timeout;
    TaskFunc _task_cd;
    bool _canceled;
    ReleaseFunc _release;

public:
    TimerTask(uint64_t id, uint32_t delay, const TaskFunc cd)
        : _id(id), _timeout(delay), _task_cd(cd), _canceled(false)
    {
    }
    void SetRelease(const ReleaseFunc &cd)
    {
        _release = cd;
    }
    uint32_t DelayTime()
    {
        return _timeout;
    }
    ~TimerTask()
    {
        if (_canceled == false)
        {
            _task_cd();
        }

        _release();
    }
    void Cancel()
    {
        _canceled = true;
    }
};
class TimerWheel
{
private:
    using WeakTask = std::weak_ptr<TimerTask>;
    using PtrTask = std::shared_ptr<TimerTask>;
    int _tick;     // 秒钟
    int _capacity; // 表盘最大数量
    std::vector<std::vector<PtrTask>> _wheel;
    std::unordered_map<uint64_t, WeakTask> _timers;
    int _timerfd; // 定时器描述符,可读事件回调就是读取计数器,执行任务
    EventLoop *_loop;
    std::unique_ptr<Channel> _timer_channel;
    void RemoveTimer(uint64_t id)
    {
        auto it = _timers.find(id);
        if (it != _timers.end())
        {
            _timers.erase(it);
        }
    }
    static int CreatTimerfd()
    {
        int timerfd = timerfd_create(CLOCK_MONOTONIC, 0);
        if (timerfd < 0)
        {
            ERR_LOG("TIMERFD_CREATE FALIED");
            abort();
        }
        struct itimerspec itm;
        itm.it_value.tv_sec = 1; // 设置第⼀次超时的时间
        itm.it_value.tv_nsec = 0;
        itm.it_interval.tv_sec = 1; // 第⼀次超时后,每隔多⻓时间超时
        itm.it_interval.tv_nsec = 0;
        timerfd_settime(timerfd, 0, &itm, NULL); // 启动定时器
        return timerfd;
    }
    int ReadTimefd()
    {
        uint64_t times;
        int ret = read(_timerfd, &times, sizeof(times));
        if (ret < 0)
        {
            ERR_LOG("READ FAILED");
            abort();
        }
        return ret;
    }
    void RunTimerTask()
    {
        _tick = (_tick + 1) % _capacity;
        _wheel[_tick].clear();
    }
    void OnTime()
    {

       int times =  ReadTimefd();
       for(int i = 0; i<times; i++)
       {
         RunTimerTask();
       }
       
    }
    void TimerAddLoop(uint64_t id, uint32_t delay, const TaskFunc &cd)
    {
        PtrTask ptr(new TimerTask(id, delay, cd));
        ptr->SetRelease(std::bind(&TimerWheel::RemoveTimer, this, id));
        _timers[id] = ptr;
        int pos = (_tick + delay) % _capacity;
        _wheel[pos].push_back(ptr);
    }
    // 刷新延迟任务
    void TimerRefreshLoop(uint64_t id)
    {
        auto it = _timers.find(id);
        if (it == _timers.end())
        {
            return;
        }
        PtrTask ptr = it->second.lock();
        int delay = ptr->DelayTime();
        int pos = (delay + _tick) % _capacity;
        _wheel[pos].push_back(ptr);
    }

    void CancelTaskLoop(uint32_t id)
    {
        auto it = _timers.find(id);
        if (it == _timers.end())
        {
            return;
        }
        PtrTask tr = it->second.lock();
        if (tr)
            tr->Cancel();
    }

public:
    TimerWheel(EventLoop *loop) : _loop(loop), _tick(0), _capacity(60), _wheel(_capacity), _timerfd(CreatTimerfd()), _timer_channel(new Channel(_loop, _timerfd))
    {
        _timer_channel->SetReadCallback(std::bind(&TimerWheel::OnTime, this));
        _timer_channel->EnableRead();
    }
    void TimerAdd(uint64_t id, uint32_t delay, const TaskFunc &cd);
    void TimerRefresh(uint64_t id);
    void TimerCancel(uint64_t id);
    bool HasTimer(uint64_t id)
    {
        auto it = _timers.find(id);
        if (it == _timers.end())
        {
            return false;
        }
        return true;
    }
};
class EventLoop
{
private:
    using Functor = std::function<void()>;
    std::thread::id _thread_id;
    int _event_fd;
    Poller _poller;
    std::unique_ptr<Channel> _event_channel;
    std::vector<Functor> _task;
    std::mutex _mutex;
    TimerWheel _time_wheel;
    void RunAllTask()
    {
        std::vector<Functor> functor;
        {
            std::unique_lock<std::mutex> _lock(_mutex);
            _task.swap(functor);
        }
        for (auto &f : functor)
        {
            f();
        }
        return;
    }
    static int CreatEventFd()
    {
        int efd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
        if (efd < 0)
        {
            ERR_LOG("EVENTFD 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 FAILED");
            abort();
        }
        return;
    }
    void WeakUpEventFd()
    {
        uint64_t val = 1;
        int ret = write(_event_fd, &val, sizeof(val));
        if (ret < 0)
        {
            if (errno == EINTR ||errno == EAGAIN)
            {
                return;
            }
            ERR_LOG("WRITE FAILED");
            abort();
        }
        return;
    }

public:
    EventLoop()
        : _thread_id(std::this_thread::get_id()), _event_fd(CreatEventFd()), _event_channel(new Channel(this, _event_fd)), _time_wheel(this)
    {
        _event_channel->SetReadCallback(std::bind(&EventLoop::ReadEventFd, this));
        _event_channel->EnableRead();
    }
    void Start()
    {
        while (1)
        {
            std::vector<Channel *> active;
            _poller.Poll(&active);
            for (auto &channel : active)
            {
                channel->HandleEvents();
            }
            RunAllTask();
        }
    }

    bool IsInloop()
    {
        return (_thread_id == std::this_thread::get_id());
    }
    void AssertInloop()
    {
        assert(_thread_id == std::this_thread::get_id());
    }
    void RunInLoop(const Functor &cd)
    {
        if (IsInloop() == true)
        {
            return cd();
        }
        return QueueInLoop(cd);
    }
    void QueueInLoop(const Functor &cd)
    {
        {
            std::unique_lock<std::mutex> _lock(_mutex);
            _task.push_back(cd);
        }
        WeakUpEventFd();
    }
    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 &cd)
    {
        _time_wheel.TimerAdd(id, delay, cd);
    }
    void TimerRefresh(uint64_t id)
    {
        _time_wheel.TimerRefresh(id);
    }
    void TimerCancel(uint64_t id)
    {
        _time_wheel.TimerCancel(id);
    }
    bool HasTimer(uint64_t id)
    {
        return _time_wheel.HasTimer(id);
    }
void Channel::Update()
{
    _loop->UpdateEvent(this);
}
void Channel::Remove()
{
    _loop->RemoveEvent(this);
}
void TimerWheel::TimerAdd(uint64_t id, uint32_t delay, const TaskFunc &cd)
{
    _loop->RunInLoop(std::bind(&TimerWheel::TimerAddLoop, this, id, delay, cd));
}
void TimerWheel::TimerRefresh(uint64_t id)
{
    _loop->RunInLoop(std::bind(&TimerWheel::TimerRefreshLoop, this, id));
}
void TimerWheel::TimerCancel(uint64_t id)
{
    _loop->RunInLoop(std::bind(&TimerWheel::CancelTaskLoop, this, id));
}
相关推荐
勿芮介1 小时前
【大模型应用】在window/linux上卸载OpenClaw
java·服务器·前端
wanhengidc1 小时前
裸金属服务器与普通服务器的区别
运维·服务器·网络·游戏·智能手机
2301_787328492 小时前
57.DevOps入门
运维·devops
野犬寒鸦2 小时前
面试常问:TCP相关(中级篇)问题原因即解决方案
服务器·网络·后端·面试
虾..2 小时前
Linux 基于TCP实现服务端客户端通信(多进程/多线程版)
java·服务器·tcp/ip
小尔¥2 小时前
LNMP环境部署
运维·数据库·nginx·php
..过云雨2 小时前
【负载均衡oj项目】01. 项目概述及准备工作
linux·c++·html·json·负载均衡
报错小能手2 小时前
nginx集群聊天室(五)nginx配置tcp服务器负载均衡
服务器·tcp/ip·nginx
李恒-聆机智能专精数采2 小时前
从零开始了解数据采集技术篇(8)——为什么工业数据采集很难用“一站式平台”解决?从设备生态到系统架构的技术分析
运维·网络·数据库·数据分析·数据采集