要求:
在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);
}