[C++]sylar高性能服务器框架学习记录:定时器模块

记录一下最近这学期学习的sylar服务器框架项目,输出整理一下项目的结构,用到的知识和自己的体会

项目仓库地址

https://github.com/sylar-yin/sylar/

整理博客过程中参考的大佬资料链接:

============================================

基础介绍

定时器模块可以实现给服务器注册定时事件,超时自动执行回调,是服务器必不可少的功能;此外sylar的方案是将定时器整合到IOManager中,通过将定时器与IOManager中的epoll_wait相结合,判断是否是定时器超时导致epoll_wait返回,如果是就把定时器回调调度到任务队列

定时器模块提供毫秒级精度,依赖Linux提供的定时机制,使用绝对时间作为基准

模块包含两个类,分别是Timer类和TimerManager类,前者为定时器类,后者为定时器管理类

Timer类

该类提供了定时器的执行/取消方法,并且在名称空间内声明了工具类Comparator用于比较定时器,成员变量存储了:

  • bool类型,标记是否是循环定时器
  • 64位无符号整数,执行周期(ms)
  • 64位无符号整数,定时器下一次超时时间(绝对时间时间戳)
  • std::function包装的回调函数
  • TimerManager* 类型的指针,指向所属TimerManagger

该类有两个构造函数,均为私有,故实例只能由友类TimerManager创建

// 定时器类
class Timer : public std::enable_shared_from_this
<Timer> {
friend class TimerManager;  
public:
    typedef std::shared_ptr
<Timer> ptr;
    bool cancel();                              // 取消定时器
    bool refresh();                             // 刷新执行时间
    bool reset(uint64_t ms, bool from_now);     // 重置定时器时间(是否从当前时间开始计算)
private:
    // 构造函数(私有)
    Timer(uint64_t ms, std::function<void()> cb,
          bool recurring, TimerManager* manager);
    // 构造函数(直接使用绝对执行时间构造)(私有)
    Timer(uint64_t next);

private:
    bool m_recurring = false;           // 是否循环定时器 
    uint64_t m_ms = 0;                  // 执行周期
    uint64_t m_next = 0;                // 精确执行时间(绝对时间)
    std::function<void()> m_cb;         // 回调函数
    TimerManager* m_manager = nullptr;  // 定时器管理器
private:
    struct Comparator {     // 比较定时器的智能指针的大小(按执行时间)
        bool operator() (const Timer::ptr& lhs, const Timer::ptr& rhs) const;
    };
};

下面对主要方法的实现进行介绍

构造函数

一种接收完整参数,另一种直接接收一个绝对时间戳

Timer::Timer(uint64_t ms, std::function<void()> cb,
          bool recurring, TimerManager* manager) 
    : m_recurring(recurring)
    , m_ms(ms)
    , m_cb(cb)
    , m_manager(manager) {
    // 执行时间为当前时间+执行周期
    m_next = sylar::GetCurrentMs() + m_ms;
}

Timer::Timer(uint64_t next) 
    : m_next(next) {
}

取消定时器

把自己从管理器中删除

// 取消定时器
bool Timer::cancel() {
    // 使用该定时器的管理器的锁
    TimerManager::RWMutexType::WriteLock lock(m_manager->m_mutex);
    if(m_cb) {
        m_cb = nullptr;
        // 在管理器类set中找到并删除自身
        auto it = m_manager->m_timers.find(shared_from_this());
        m_manager->m_timers.erase(it);
        return true;
    }
    return false;
}

刷新定时器进度

先从管理器中删除自己,修改自己后再添加回去

// 刷新执行时间
bool Timer::refresh() {
    // 使用该定时器的管理器的锁
    TimerManager::RWMutexType::WriteLock lock(m_manager->m_mutex);
    if(!m_cb) {
        return false;
    }
    // 在管理器的set中找到自身并删除
    auto it = m_manager->m_timers.find(shared_from_this());
    if(it == m_manager->m_timers.end()) {
        return false;
    }
    m_manager->m_timers.erase(it);
    // 重置下次超时绝对时间,重新添加自身到管理器set中
    m_next = sylar::GetCurrentMs() + m_ms;
    m_manager->m_timers.insert(shared_from_this());
    return true;
}

重置定时器执行周期

逻辑与上面类似,可以选择是否立即按照新周期开始执行还是下一个周期开始执行新周期值

// 重置定时器超时周期(是否从现在开始)
bool Timer::reset(uint64_t ms, bool from_now) {
    if(ms == m_ms && !from_now) {   
        return false;
    }
    TimerManager::RWMutexType::WriteLock lock(m_manager->m_mutex);
    if(!m_cb) {
        return false;
    }
    // 在管理器中找到并删除自身
    auto it = m_manager->m_timers.find(shared_from_this());
    if(it == m_manager->m_timers.end()) {
        return false;
    }
    m_manager->m_timers.erase(it);
    // 根据是否从现在开始设置值,重新添加自己
    uint64_t start = 0;
    if(from_now) {
        start = sylar::GetCurrentMs();
    } else {
        start = m_next - m_ms;
    }
    m_ms = ms;
    m_next = start + m_ms;
    m_manager->addTimer(shared_from_this(), lock);
    return true;
}

Comparator比较结构体

优先比较超时值大小,如果过相同则比较指针值

bool Timer::Comparator::operator() (const Timer::ptr& lhs
                                    , const Timer::ptr& rhs) const {
    if(!lhs && !rhs) {
        return false;
    }
    if(!lhs) {
        return true;
    }
    if(!rhs) {
        return false;
    }
    if(lhs->m_next < rhs->m_next) {
        return true;
    }
    if(rhs->m_next < lhs->m_next) {
        return false;
    }
    return lhs.get() < rhs.get();
}

TimerManager定时器管理类

提供了添加/删除(条件)定时器,返回距离最近超时时间,获取超时定时器回调函数数组等方法

此外还支持了检测系统时间是否被人为调整的功能,并制定了应对策略

成员变量存储了:

  • 读写锁
  • 一个存放着Timer智能指针的std::set,传入TImer比较结构体,构成按照最近超时为标准的小根堆
  • 一个记录是否触发IOManager的tickle的bool变量
  • 64位无符号整数,记录启动时的时间戳
// 定时器管理器类
class TimerManager {
friend class Timer;
public:
    typedef RWMutex RWMutexType;

    TimerManager();             // 构造函数
    virtual ~TimerManager();    // 虚析构函数

    // 添加定时器
    Timer::ptr addTimer(uint64_t ms, std::function<void()> cb
                        , bool recurring = false);
    // 添加条件定时器
    Timer::ptr addConditionTimer(uint64_t ms, std::function<void()> cb
                        , std::weak_ptr
<void> weak_cond
                        , bool recurring = false);
    // 返回最近一个定时器的执行时间间隔
    uint64_t getNextTimer();
    // 获取需要执行回调的定时器数组
    void listExpiredCb(std::vector<std::function<void()>>& cbs);
    // 是否有定时器
    bool hasTimer();
protected:
    // 有新定时器插入到定时器首部时,执行该函数
    virtual void onTimerInsertAtFront() = 0;
    // 将定时器添加到管理器中
    void addTimer(Timer::ptr val, RWMutexType::WriteLock& lock);
private:
    // 检测服务器时间是否被调后
    bool detectClockRollover(uint64_t now_ms);
private:
    RWMutexType m_mutex;
    std::set<Timer::ptr, Timer::Comparator> m_timers;   // 定时器集合,方便获取最近超时定时器
    bool m_tickled = false;                             // 是否触发onTimerInsertAtFront()
    uint64_t m_previousTime = 0;                        // 上次执行时间
};

下面对主要方法进行介绍

构造函数

初始化m_previousTime变量

TimerManager::TimerManager() {
    m_previousTime = sylar::GetCurrentMs();
}

析构函数

没有写逻辑,但由于要被IOManager继承所以声明为virtual函数

TimerManager::~TimerManager() {
}

添加定时器

创建Timer实例并添加到set中,如果是当前最快超时定时器还要通知IOManager执行tickle

// 添加定时器
Timer::ptr TimerManager::addTimer(uint64_t ms, std::function<void()> cb
                        , bool recurring) {
    Timer::ptr timer(new Timer(ms, cb, recurring, this));
    RWMutexType::WriteLock lock(m_mutex);
    addTimer(timer, lock);
    return timer;
}
// 添加定时器(内部方法)
void TimerManager::addTimer(Timer::ptr val, RWMutexType::WriteLock& lock) {
    auto it = m_timers.insert(val).first;
    // 如果是超时时间最近的定时器并且没有设置触发onTimerInsertAtFront(),设置m_tickle为true
    bool at_front = (it == m_timers.begin()) && !m_tickled;
    if(at_front) {
        m_tickled = true;
    }
    lock.unlock();

    // 触发onTimerInsertAtFront()
    if(at_front) {
        onTimerInsertAtFront();
    }
}

添加条加定时器

条件用传入的std::weak_ptr<>指向内存是否释放表示

// 添加条件定时器
Timer::ptr TimerManager::addConditionTimer(uint64_t ms, std::function<void()> cb
                                           , std::weak_ptr
<void> weak_cond
                                           , bool recurring) {
    return addTimer(ms, std::bind(&OnTimer, weak_cond, cb), recurring);
}

static void OnTimer(std::weak_ptr
<void> weak_cond, std::function<void()> cb) {
    // 若weak_cond指向的对象未析构,则返回非空
    std::shared_ptr
<void> tmp = weak_cond.lock();
    if(tmp) {
        cb();   // 若对象还在,执行cb
    }
}

返回距离最近定时器超时的时间间隔

如下

// 返回最近一个定时器的执行时间间隔
uint64_t TimerManager::getNextTimer() {
    RWMutexType::ReadLock lock(m_mutex);
    m_tickled = false;
    // 若没有定时器,返回极大值
    if(m_timers.empty()) {
        return ~0ull;   // ull 0 取反是最大
    }

    // 获取最快超时的定时器
    const Timer::ptr& next = *m_timers.begin();
    uint64_t now_ms = sylar::GetCurrentMs();
    if(now_ms >= next->m_next) {
        return 0;                       // 若已经超时,返回0
    } else {
        return next->m_next - now_ms;   // 若还没超时,返回距离超时剩余时间
    }
}

获取已超时定时器回调函数数组

使用迭代器操作

// 获取需要执行回调的定时器数组
void TimerManager::listExpiredCb(std::vector<std::function<void()>>& cbs) {
    uint64_t now_ms = sylar::GetCurrentMs();
    std::vector<Timer::ptr> expired;
    {
        RWMutexType::ReadLock lock(m_mutex);
        if(m_timers.empty()) {
            return;
        }
    }
    RWMutexType::WriteLock lock(m_mutex);

    // 若系统时间没被调后
    bool rollover = detectClockRollover(now_ms);
    if(!rollover && ((*m_timers.begin())->m_next > now_ms)) {
        return; // 如果服务器时间没被调,并且最快超时定时器还没到超时时间,直接返回空数组
    }

    // 定一个超时时间为现在的定时器
    Timer::ptr now_timer(new Timer(now_ms));
    // 若系统时间被调,则将全部视为超时,否则正常找超时的定时器
    auto it = rollover ? m_timers.end() : m_timers.lower_bound(now_timer);
    while(it != m_timers.end() && (*it)->m_next == now_ms) {
        it++;
    }
    // 将超时定时器插入expired,并从m_timers中删除
    expired.insert(expired.begin(), m_timers.begin(), it);
    m_timers.erase(m_timers.begin(), it);
    cbs.reserve(expired.size());

    // 将筛选出的定时器的回调函数插入到cbs
    for(auto& timer : expired) {
        cbs.push_back(timer->m_cb);
        if(timer->m_recurring) {    
            timer->m_next = now_ms + timer->m_ms;
            m_timers.insert(timer);     // 如果是循环定时器,重新加入set
        } else {
            timer->m_cb = nullptr;
        }
    }
}

检测系统时间是否被调

若当前时间比定时器模块启动时的时间还晚一小时视为被调

// 检测服务器时间是否被调后
bool TimerManager::detectClockRollover(uint64_t now_ms) {
    bool rollover = false;
    // 若当前时间小于m_previousTime并且慢了超过一小时
    if(now_ms < m_previousTime &&
            now_ms < (m_previousTime - 60 * 60 * 1000)) {
        rollover = true;
    }
    // 设置m_previousTime为当前时间
    m_previousTime = now_ms;
    return rollover;
}

TimerManager与IOManager整合

IOManager公有继承TimerManager

class IOManager : public Scheduler, public TimerManager {
    // ...
}

在idle中增加定时器处理逻辑,将epoll_wait函数的超时时间设置为TimerManagger距离最近一个定时器超时的时间;后续需要判断是否是由于定时器超时返回,如果是则批量调度已经超时定时器的回调函数

while(true) {
        // 检查是否需要停止IOManager
        uint64_t next_timeout = 0;
        if(stopping(next_timeout)) {
            SYLAR_LOG_INFO(g_logger) << "name=" << getName() << " idle stopping exit";
            break;
        }

        int rt = 0;
        do {
            static const int MAX_TIMEOUT = 3000;    // 最大等待超时时间
            // 处理下一个定时器超时时间
            if(next_timeout != ~0ull) {
                next_timeout = (int)next_timeout > MAX_TIMEOUT
                                ? MAX_TIMEOUT : next_timeout;
            } else {    
                next_timeout = MAX_TIMEOUT;
            }

            // 调用epoll_wait等待IO事件
            rt = epoll_wait(m_epfd, events, 64, (int)next_timeout);

            // 如果epoll被中断则回到循环头部继续等待
            if(rt < 0 && errno == EINTR) {
            } else {    // 否则跳出循环
                break;
            }
        } while(true);

        std::vector<std::function<void()>> cbs;
        listExpiredCb(cbs);
        if(!cbs.empty()) {
            // SYLAR_LOG_DEBUG(g_logger) << "on timer cbs.size=" << cbs.size();
            schedule(cbs.begin(), cbs.end());
            cbs.clear();
        }

        // 遍历epoll_wait返回的事件
        // ...
}

测试

添加1000毫秒循环定时器,超时5次之后将超时时间设置为2000毫秒

#include "sylar/sylar.h"
#include "sylar/iomanager.h"
#include 
<iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>

sylar::Logger::ptr g_logger(SYLAR_LOG_ROOT());

sylar::Timer::ptr s_timer;
void test_timer() {
    sylar::IOManager iom(1, false, "test_timer");
    s_timer = iom.addTimer(1000, [](){
        static int i = 0;
        SYLAR_LOG_INFO(g_logger) << "hello timer i=" << i;
        if(++i == 5) {
            //s_timer->cancel();
            s_timer->reset(2000, true);
        }
    }, true);
}

int main(int argc, char** argv) {
    SYLAR_LOG_INFO(g_logger) << "test begin";
    test_timer();
    SYLAR_LOG_INFO(g_logger) << "test end";
    return 0;
}

可以看到程序行为符合预期

image-20260207200013201

总结

为框架实现了定时器功能,并整合如IOManager中

评论

  1. sankkooos
    Android Chrome 142.0.7444.173
    2 月前
    2026-2-08 1:17:21

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇