定时器浅析
定时器是服务器中一个很重要的部件,比如定时对连接的进行健康检测,在设计协程的sleep API时也需要定时器的介入,因此有效地组织这些定时事件,使之能在预期的时间点被触发且不影响服务器的主要逻辑,对于服务器的性能有着至关重要的影响。为此,定时器的设计值得我们去深入学习,本文将浅析定时器的设计及其在不同数据结构下的表现。
Timer
话不多说,直接上代码,从源码开始分析,这是我的一个服务器框架的定时器。
class TimeManager; // 定时器管理器
class Timer : public std::enable_shared_from_this<Timer>{ // 定时器
friend class TimeManager;
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 recurring, TimeManager* timeManager);
Timer(uint64_t next);
private:
uint64_t m_ms = 0; //执行周期
uint64_t m_next = 0; //精确的执行时间
std::function<void()> m_cb; //定时器回调
bool m_recurring = false; //是否循环
TimeManager* m_manager = nullptr;
private:
struct Compare{
bool operator()(const Timer::ptr& lhs, const Timer::ptr& rhs) const;
};
};
定时器其实并不只指定时器,其实还包括定时器容器(TimeManager
),定时器容器的设计正式定时器的精妙所在。
从上面的代码我们可以看到,定时器有五个私有变量,分别是:
m_ms
: 定时器的执行周期m_next
: 定时器事件还有多久时间就会执行m_cb
: 定时器回调函数m_manager
: 定时器到达时间执行事件后是否继续定时
还有定义了私有类Compare
是供TimeManager
调用的,后文再说它的设计。
三个公有接口:
cancel()
: 取消定时器事件refresh()
: 重新计算时间reset()
: 重新设置定时器的执行周期,可以选择是否重新计时
这里有一个细节,就是构造函数是私有的,切TimeManager
被设置成友元类,所以Timer
只能通过TimeManager
创建
下面是公有接口的实现代码:
cancel()
bool Timer::cancel() {
TimeManager::MutexType::Lock lock(m_manager->m_mutex);
if(m_cb){
auto it = m_manager->m_timers.find(shared_from_this());
m_manager->m_timers.erase(it);
return true;
}
return false;
}
m_manager->mutex
是TimeManager
用来保护计时器容器的互斥量,保证线程安全。如果有回调函数的话,则从TimeManager的容器中删除掉本身。