基于 stl map 的定时器

2021-04-08

项目要添加 session,每个 session 需要设置过期时间,所以需要定时器。

定时器实现逻辑:对每个定时器事件到期时间进行排序,对有序数据进行顺序检查处理,需要支持查找。

比较了多种方案后,决定通过参考 C++11实现的定时器 ,基于 stl 的字典(map)造个简单点的轮子。


1. 实现

方案选择 std::map 实现。原因:

  • std::map 内部是一颗红黑树,读写时间复杂度 O(logN)。
  • std::map 迭代器默认是根据 key 索引的中序排列。
  • 支持查询。

实现源码:timers.h, timers.cpp

测试源码:test_timers.cpp


1.1. 定时器事件

事件组合 id(TimerGrpID)设计,主要为了方便 std::map 内部排序,还有去重。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* 定时器事件组合 id。
 * 第一个参数是事件到期时间,方便 std::map 排序。
 * 第二个参数是定时器事件 id。*/
typedef std::pair<int64_t, int> TimerGrpID;

/* 定时器事件回调函数。
 * 第一个参数:定时器 id。
 * 第二个参数:是否循环定时器。
 * 第三个参数:创建定时器时传入的用户参数。*/
typedef std::function<void(int, bool, void*)> TimerEvent;

/* 定时器事件。 */
class Timer {
    ...
    int m_id = 0;               /* timer's id. */
    uint64_t m_after_time = 0;  /* timeout in `after` milliseconds. */
    uint64_t m_repeat_time = 0; /* repeat milliseconds. */
    void* m_privdata = nullptr; /* user's data. */
    TimerEvent m_callback_fn;   /* callback function. */
};

1.2. 定时器事件管理

主要通过两个数据结构进行维护:std::mapstd::unordered_map,前者方便定时器事件的排序和数据存储,后者方便查询定时事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Timers {
    ...
   public:
    /* 删除定时器事件。 */
    bool del_timer(int id);
    /* 添加定时器事件。 */
    int add_timer(const TimerEvent& fn, uint64_t after, uint64_t repeat = 0, void* privdata = nullptr);

   public:
    /* 外部定时刷新,检查事件是否过期. */
    virtual void on_repeat_timer() override;
    ...
   protected:
    int m_last_timer_id = 0;
    std::map<TimerGrpID, Timer*> m_timers;
    std::unordered_map<int, TimerGrpID> m_ids; /* 通过哈希表方便对 std::map 的组合 id 进行查询,删除. */
};

2. 参考