仿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 小时前
mnt_init 函数
linux
律宏阔7 小时前
WSL Docker 端口明明空闲,但就是绑不上端口
linux·windows
律宏阔7 小时前
WSL 突然断网,无法 ping 内网或外网
linux·windows
穷人小水滴7 小时前
用容器编译 VirtualBox 虚拟机软件 (ArchLinux, podman)
linux·容器·virtualbox
乱码三千8 小时前
如何优雅地直连无公网 IP 的远程 GPU 服务器
linux·人工智能·程序员
yunwei378 小时前
使用 eBPF 跟踪 Nginx 请求
linux·后端·性能优化
yunwei378 小时前
使用 eBPF 跟踪 MySQL 查询
linux·后端·性能优化
yunwei378 小时前
eBPF 示例教程:使用 XDP 捕获 TCP 信息
linux·后端·性能优化
judezh8 小时前
api 容器一直 unhealthy?我把一个 Agent 运行时的健康检查逐条拆了,查出四个问题
运维·docker
GeW8 小时前
制造业高端化,为什么必须重估Linux和数据库?
linux