1️⃣ 什么是定时器

定时器( Timer )本质上就是一种“按时间触发任务”的机制:你把一个任务 task 交给它,并告诉它“多久以后执行”或者“每隔多久执行一次”,定时器负责在合适的时间点把这个任务触发出来。

你可以把 Timer 理解成一个“闹钟”,而 TimerManager 就是“闹钟管理器”:你只负责设定时间和要做的事,到点后由它统一唤醒并执行对应任务。

在服务器/网络框架(比如 Sylar )里,定时器通常用来做这些事:

  • 超时控制:连接超时、读写超时、RPC 超时重试
  • 周期任务:心跳包、定期统计、定期清理缓存/过期会话
  • 延迟执行:延迟关闭连接、延迟回收资源、延迟投递任务
  • 事件循环协作:让 epoll/selectI/O 等待不会“傻等”,而是能在最近的定时点醒来。具体来说,事件循环会通过 getNextTimer() 计算“距离最近一次定时任务还要等多久”,并把这个时间作为 epoll_waittimeout,这样既能等待 I/O 事件,又不会错过定时器到期触发。

2️⃣ 整体框架

框架图

3️⃣ 定时器模块

私有成员

  • m_ms:执行周期
  • m_next: 精确的执行时间
  • m_cb:定时器绑定的回调任务
  • m_manager:定时器管理类
  • recurring:是否是循环定时器

重要成员函数

  • 构造函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Timer::Timer(bool recurring, uint64_t ms, std::function<void()> cb, TimerManager* manager)
    : recurring(recurring)
    , m_ms(ms)
    , m_cb(cb)
    , m_manager(manager)
    {
    m_next = sylar::GetCurrentMS() + m_ms ;
    }

    • 作用
      • 当前定时器的触发时间:
        • m_next = GetCurrentMS() + m_ms
        • 含义:从当前时间起延迟 m_ms 毫秒执行回调 m_cb
  • cancel

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    bool Timer::cancel()
    {
    // 在多线程环境下,可能有多个线程会对 TimerManager 进行操作,所以需要加锁
    TimerManager::RWMutexType::WriteLock Lock(m_manager->m_mutex);
    if (m_cb) // 判断定时器是否有回调任务,没有直接返回false
    {
    // 如果有回调任务,将其置空
    m_cb = nullptr;
    // 在TimerManager中寻找Timer,将其删除
    auto it = m_manager->m_timers.find(shared_from_this());
    if (it != m_manager->m_timers.end())
    {
    // 这说明找到了,将其从中删除
    m_manager->m_timers.erase(it);
    }
    return true;
    }
    return false;
    }
    • 作用
      • 这个函数主要用于取消当前定时器,让它不再被触发执行,并把它从 TimerManager 的定时器集合中移除。
  • refresh

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    bool Timer::refresh()
    {
    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())
    {
    // 这说明定时器列表中存在该定时器,将其从中删除
    m_manager->m_timers.erase(it);
    // 重置执行时间
    m_next = sylar::GetCurrentMS() + m_ms;
    // 将该定时器重新添加到定时器列表中
    m_manager->m_timers.insert(shared_from_this());
    return true;
    }
    return false;
    }
    • 作用
      • 这个函数主要用于刷新当前定时器以及定时器绑定的时间,并把它重新插入到 TimerManager
  • reset

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    bool Timer::reset(uint64_t ms, bool from_now)
    {
    // 这说明定时器的时间间隔相同,而且不从现在开始重新及时
    if (ms == m_ms && !from_now)
    {
    return true;
    }
    // 添加锁来进行线程之间的同步
    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_next : 是下次定时器的触发时间,m_ms: 是定时器到下次触发时间的时间间隔
    }
    m_ms = ms;
    m_next = m_ms + start;
    m_manager->addTimer(shared_from_this(), Lock);
    return true;
    }
    • 作用
      • 如果不要求从现在重新定时且前后两次定时的时间间隔相同,就直接返回。
      • 否则重新计算时间并添加到定时器集合

4️⃣ 定时器管理模块

私有成员

  • m_mutex:读写锁互斥量
  • m_timers:定时器集合
  • m_previousTime:上次定时器执行时间
  • m_tickled:避免了在插入新的最早定时器时,每次都重新触发通知,而只会在第一次插入时处理它

重要成员函数

  • 构造函数

    1
    2
    3
    4
    TimerManager::TimerManager()
    {
    m_previousTime = GetCurrentMS();
    }
    • 作用
      • 记录 TimerManager 被创建时的系统运行时间(以毫秒为单位),用于后续定时器逻辑中检测系统时间是否出现回退、跳变等问题。
  • addTimer

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Timer::ptr TimerManager::addTimer(uint64_t ms, bool recurring, std::function<void()> cb)
    {
    // 构造一个定时器
    Timer::ptr timer(new Timer(recurring, ms, cb, this));
    // 创建一个写者锁
    TimerManager::RWMutexType::WriteLock Lock(m_mutex);
    // 将定时器添加到定时器列表中
    addTimer(timer,Lock);
    // 返回定时器
    return timer;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    void TimerManager::addTimer(Timer::ptr val, RWMutexType::WriteLock& Lock)
    {
    auto it = m_timers.insert(val).first;
    bool at_front = (it == m_timers.begin()) && !m_tickled;
    if(at_front)
    {
    m_tickled = true;
    }
    Lock.unlock();

    if(at_front)
    {
    onTimerInsertAtFront();
    }
    }
    • 作用
      • 通过设置的时间间隔 ms,创建一个定时器 Timer,并将其添加到定时器集合 m_timers
      • 如果这个新插入的定时器位于定时器集合中的最开始位置,即这个定时器最早到期。那么就必须通知 IO 线程提前醒来,比如:
        • 原来最早到期:还有 5000ms
        • 现在插入了一个更早到期的 timer:只剩 10ms
        • 如果不唤醒 IO 线程,它还会继续睡到 5000ms 才醒 → 这个 10mstimer 就会被延迟执行。
      • 为什么需要 m_tickled
        • m_tickled == false:说明当前还没有“唤醒请求”在路上
          • 第一个插到最前面的 timer 负责唤醒,并把 m_tickled 置为 true
        • m_tickled == true:说明已经有人触发过唤醒了(IO 线程马上就会醒/正在醒)
          • 后续再插更早的 timer 不需要重复唤醒,因为 IO 线程醒来后会看到集合里最新最早的那个 timer
  • addTimerCondition

    1
    2
    3
    4
    Timer::ptr TimerManager::addTimerCondition(uint64_t ms, bool recurring, std::function<void()> cb, std::weak_ptr<void> weak_cond)
    {
    return addTimer(ms, recurring, std::bind(&onTimer,weak_cond, cb));
    }
  • onTimer

    1
    2
    3
    4
    5
    6
    7
    8
    9
    static void onTimer(std::weak_ptr<void> weak_cond, std::function<void()> cb)
    {
    // 这用来检测智能指针的指向的对象是否存在(通过弱智能指针weak_ptr不会使引用计数 + 1的特点)
    std::shared_ptr<void> tmp = weak_cond.lock();
    if (tmp)
    {
    cb();
    }
    }
    • 作用:
      • 添加一个条件定时器——只有当某个对象(由 weak_cond 指向)仍然存活时,定时器到期才会真正执行回调 cb;如果对象已经析构,则回调不会执行,相当于自动失效。
  • listExpireCb

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    void TimerManager::listExpireCb(std::vector<std::function<void()>>& cbs)
    {
    // 获得系统到现在的总时间
    uint64_t now = GetCurrentMS();
    // 定义一个超时定时器的列表,用来存放时间超过现在的定时器
    std::vector<Timer::ptr> expired;
    // 通过加读锁来判断当前定时器列表是否为空
    {
    RWMutexType::ReadLock Lock(m_mutex);
    if (m_timers.empty())
    {
    return;
    }
    }
    // 因为要对定时器列表进行删除,所以加写锁来进行控制、
    RWMutexType::WriteLock Lock(m_mutex);
    if (m_timers.empty())
    {
    return;
    }
    // 定义回滚变量
    bool rollover = false;
    // 检测是否出现回滚
    if (detectClockRollover(now))
    {
    rollover = true;
    }
    // 这说明没有出现回滚,但是最近的定时器的触发时间都大于当前系统时间
    if (!rollover && ((*m_timers.begin())->m_next > now))
    {
    return;
    }
    // 定义一个当前时间的定时器
    Timer::ptr nowTimer(new Timer(now));
    // 根据是否回滚来执行不同的操作
    auto it = rollover ? m_timers.end() : m_timers.lower_bound(nowTimer);
    // 找到第一个大于当前时间的定时器
    while(it != m_timers.end() && (*it)->m_next == now)
    {
    ++it;
    }
    // 将超时定时器全部添加到超时计时器列表中
    expired.insert(expired.begin(), m_timers.begin(), it);
    // 将超时定时器从原来的列表中删除
    m_timers.erase(m_timers.begin(), it);
    // 给回调函数集合扩容
    cbs.resize(expired.size());
    // 遍历超时定时器集合,将超时定时器的回调函数全部添加到回调函数集合中
    for (auto& timer : expired)
    {
    cbs.push_back(timer->m_cb);
    // 判断是不是循环定时器
    if (timer->recurring)
    {
    timer->m_next = now + timer->m_ms;
    m_timers.insert(timer);
    }
    else
    {
    timer->m_cb = nullptr;
    }
    }
    }
    • 作用
      • 批量取出当前已经到期的定时器回调,把它们放进 cbs 供外部执行
      • 把这些到期定时器从管理器中移除
      • 对循环定时器重新计算下一次触发时间再插回去;另外还处理“系统时间回拨”的特殊情况。

参考文章: