要求:
在HTTP服务器上实现一个不需要精确定时的Timer,该Timer本质上是一堆时间事件的管理类,每个时间事件都绑定一个事件回调函数,可以scedule在某个时间点开始运行,运行一次或者以固定时间间隔反复执行。Timer的使用者可以以代码方式加载或者撤销指定的时间事件。
设计这个Timer的原因,因为一个HTTP服务器上加载了诸多服务,我们需要定时收集,统计系统MEMORY,CPU, LOCK,QPS, LATENCY等信息,同时我们希望统计这些资源是如何在各模块中分配的,有了这些信息,才能方便进行trouble shooting和performance tuning。
设计:
从逻辑上来说,每一个事件包括以下信息:开始执行时间,执行间隔,是否需要repeat
execution,
绑定的事件回调函数…在C++的实现中,我们往往把回调函数设计成一个类(我们可以在类实例中存储状态,这样比单纯的回调函数更加灵活),看下面的接口:
class TimeoutCallback{
public:
virtual
~TimeoutCallBack(){};
virtual void CallbackFunc() = 0;
//纯虚接口,所有定制的callback都继承TimeoutCallback并实现自己的CallbackFunc。
};
一个TimeoutCallback实例可以被绑定在多个事件上,下面是事件实现类(为了演示方便,这里都声明成public)
class TimeEvent{
public:
time_t m_schedule;
//预计下一次的执行时间
time_t m_interval;
//如果是repeatable的事件,事件的执行间隔
bool m_repeatable;
//是不是需要重复执行
int m_count; //该事件被触发的次数
TimeoutCallback* m_callback; //绑定的事件回调类实例
};
而Timer类需要提供事件的注册,撤销以及执行功能。
class TimerServer{
public:
void Register(TimeEvent*
eventInst); //将一个事件加入执行队列
void Release(TimeEvent* eventInst);
//将一个事件从执行队列中移走
void StartServer();
//启动Timer
void Shutdown();
//关闭Timer
private:
std::list<TimeEvent*> m_events;
//事件的执行队列
pthread_t m_thread; //保存执行函数的thread
id,以便执行join操作
pthread_cond_t m_cond;
pthread_mutex_t
m_mutex; //用于保护eventQueue
m_shutdown;
//关闭标志
private:
static void* ExecuteFunc(void* ptr);
//执行函数
};
可见这里的设计策略是由一个单独的执行线程不停的执行事件队列里的事件。这个方法无法实现精确定时。但是已经能满足要求了。
实现:
下面给出TimerServer的实现代码
void TimerServer::StartServer(){
… //讲pthread_attr_t
attr配置成PTHREAD_CREATE_JOINABLE
pthread_create(&m_thread,
&attr, ExecuteFunc, this); //启动执行线程
}
void TimerServer::Register(TimeEvent* eventInst){
MutexGuard(&m_mutex); //保护执行队列,使用单独的执行线程使得我们的应用已经是多线程的。
m_events.push_back(eventInst);
}
void* TimerServer::ExecuteFunc(void* ptr){
TimerServer* self =
(TimerServer*)ptr; //使用C++ static成员函数作为线程函数的典型做法
while(!
self->m_shutdown){
//这里的while循环说明,我们将m_shutdown置true以后并不马上生效,允许把当前这轮执行完
time_t
now=time(0); //拿到当前时间
std::list<TimeEvent*>::iterator iter=
m_events.begin();
for(; iter!=m_events.end(); iter++){
//迭代所有的事件
TimerServer* cur = *iter;
if(now < cur->m_schedule) continue; //未来的事件,暂时跳过
if(cur->m_repeatable || cur->m_count <= 0){
//如果不是repeat的任务并且已经执行过了,也跳过
if(cur->m_callback)
cur->m_callback->CallbackFunc
();
cur->m_count++;
if(cur->repeatable)
cur->m_schedule += cur->m_interval;
}
}
while(!self->m_shutdown){
//我们不能让ExecuteFunc耗尽所有的CPU资源,于是sleep一会儿
if(m_cond.wait(m_kSleepbetween)==ETIMEDOUT) break; //使用conditional
var来同步
}
}
}
关于TimerServer::ExecuteFunc的实现我有两点要说明:
- 上面的实现是线程不安全的,所有的list操作都需要mutex保护
- 条件变量的wait要结合TimerServer::Shutdown来理解
void TimerServer::Shutdown(){
if(m_shutdown) return;
m_shutdown = true;
m_cond.signal();
pthread_join(m_thread);
}