【动手写协程库】系列笔记是学习sylar的协程库时的记录,参考了从零开始重写sylar C++高性能分布式服务器框架和代码随想录中的文档。文章并不是对所有代码的详细解释,而是为了自己理解一些片段所做的笔记。
TimerManager
类中具体定义实现可以在这里查看:Github: src/timer.cpp
通过定时器,我们可以实现给服务器注册定时事件。sylar的定时器采用最小堆设计,所有定时器根据绝对的超时时间点(也就是超时到期的具体时间戳)进行排序,每次取出离当前时间最近的一个超时时间点,计算出超时需要等待的时间,然后等待超时。超时时间到后,获取当前的绝对时间点,然后把最小堆里超时时间点小于这个时间点的定时器都收集起来,执行它们的回调函数。
定时器相关API如下:
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 64 65 66 67 68 69 70
| class Timer : public std::enable_shared_from_this<Timer> { friend class TimerManager;
public: using Ptr = std::shared_ptr<Timer>;
bool Cancel(); bool Refresh(); bool Reset(uint64_t ms, bool from_now);
private: Timer(uint64_t ms, std::function<void()> cb, bool recur, TimerManager* manager); Timer(uint64_t next);
private: bool is_recur_; uint64_t exec_cycle_; uint64_t next_; std::function<void()> callback_; TimerManager* manager_;
struct Comp { bool operator()(const Timer::Ptr& lt, const Timer::Ptr& rt) const { if (!lt || !rt) { return !lt && rt; } return lt->next_ < rt->next_; } }; };
class TimerManager { friend class Timer;
public: TimerManager(); virtual ~TimerManager();
Timer::Ptr AddTimer(uint64_t ms, std::function<void()> cb, bool is_recur = false);
Timer::Ptr AddConditionTimer(uint64_t ms, std::function<void()> cb, std::weak_ptr<void> cond, bool is_recur = false);
uint64_t GetNextTimerInterval();
std::vector<std::function<void()>> GetExpiredCbList();
bool HasTimer();
protected: virtual void OnTimerInsertAtFront() = 0;
void AddTimer(Timer::Ptr timer, std::shared_lock<std::shared_mutex>& lock);
private: bool DetectClockRollover(uint64_t now_ms);
private: std::shared_mutex rw_mutex_; std::set<Timer::Ptr, Timer::Comp> timer_heap_; bool is_tickled_; uint64_t pre_exec_time_; };
|
个人感觉最重要的API是AddTimer
、GetNextTimerInterval
和GetExpiredCbList
。
AddTimer
向时间堆中添加超时超时时间到了后的回调函数(利用Timer
类来封装)。
GetNextTimerInterval
用于获取下一个定时器到现在的执行间隔时间,这会用于IOManager::Idle()
中用于与规定的最大超时时间进行比较,用较小者作为epoll_wait
的超时时间参数。
GetExpiredCbList
获取的是所有超时定时器的回调函数。在IOManager::Idle()
会调用这个函数将所有超时定时器的回调函数作为调度任务加入任务队列进行处理。
在定时器中,使用GetElapsedMS()
来获取系统自启动来经过的时间,其内部使用clock_gettime
来获取时间,这相比于一些传统时间获取函数(如time
或gettimeofday
)有更高的精度:
1 2 3 4 5 6
| static uint64_t GetElapsedMS() { struct timespec ts = {0}; clock_gettime(CLOCK_MONOTONIC_RAW, &ts); return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; }
|