毫秒級定時器模塊的設計與實現


0、引言

  定時器在服務器的通信模塊中會廣泛使用到,通過定時器可以相應的高效實現業務邏輯。由於一般給出的定時器都是以秒作為最小單元來處理的,大部分場景能夠滿足要求,但在一些特殊場景需要實現更精確的定時任務,這時候,就有必要去構建一個毫秒級的定時管理模塊。因而本文分享了一種定時器管理模塊的實現方法,同時給出了相應的使用案例,希望對讀者有一定的幫助。

1、毫秒級的時間類型

首先構建一個毫秒級的類型,並對相應的運算符進行了重載,具體代碼示例如下:

#ifdef LINUX
    #include <sys/time.h>
#endif

#ifdef IOS
    #include <sys/_types/_timeval.h>
#endif

#ifdef WIN32
    #include <winsock.h>
#endif

class Time
{
private:
    struct timeval time_val;
public:
    Time();
    ~Time();
    Time(const Time& other);
    Time(const struct timeval& timeVal);
    Time& operator=(const Time& other);
    Time& operator=(const struct timeval& timeVal);
    bool operator==(const Time& other);
    bool operator<(const Time& other);
    Time& operator+=(int val);

    time_t getSecond();
}

Time::Time()
{
    memset(time_val, 0, sizeof(struct timeval));
}
Time::Time(const Time& other)
{
    time_val = other.time_val;
}

Time::Time(const struct timeval& timeVal)
{
    time_val = timeVal;
}
Time& operator=(const Time& other)
{
    time_val = other.time_val;
    return *this;
}
Time& operator=(const struct timeval& timeVal)
{
    time_val = timeVal;
    return *this;
}
bool operator==(const Time& other)
{
    if (time_val.tv_sec != other.time_val.tv_sec ||
        time_val.tv_usec != other.time_val.tv_usec )
    {
        return false;
    }
    return true;
}
bool operator<(const Time& other)
{
    if (time_val.tv_sec < other.time_val.tv_sec)
    {
        return true;
    }
    else if (time_val.tv_sec == other.time_val.tv_sec)
    {
        if (time_val.tv_usec < other.time_val.tv_usec)
        {
            return true;
        }
    }
    return false;
}
Time& operator+=(int val)
{
    time_val.tv_usec += val*1000;
    time_val.tv_sec += time_val.tv_usec/1000000;
    time_val.tv_usec = time_val.tv_usec % 1000000;
}
time_t getSecond()
{
    return time_val.tv_sec;
}

 

2.定時任務的基類

  這可作為其他具體業務類型的基類,若該業務類型需要使用到定時器來執行定時任務,則應繼承該類型。

class TimerObject
{
public:
    TimerObject(){};
    virtual ~TimerObject(){};
    virtual void DoSomething(int time_id, Time time_val); //當定時觸發時,執行該函數
}

 

3.定時器節點的數據結構   

每個定時器對應一個定時器節點,相關參數有:每次定時的間隔時間、是否無限循環、觸發的次數、具體執行定時任務的對象等

struct TimerNode
{
    int id;                //定時器節點的唯一標識
    int interval;        //定時的時間間隔
    Time startTime;        //開始定時的時間
    bool isForever;        //是否無限循環
    int totalCount;        //定時的總次數
    int curCount;        //當前定時已觸發的次數
    TimerObject* obj;    //所觸發定時任務的對象
    TimerNode* preTimerNode;    //使定時器節點之間形成一個鏈表
    TimerNode* nextTimerNode;
    TimerNode()
    {
        id = 0;
        interval = 0;
        isForever = false;
        totalCount = 0;
        curCount = 0;
        obj = NULL;
        preTimerNode = NULL;
        nextTimerNode = NULL;
    }
}

 

4.定時器管理的類型   

保存所有的定時器節點,並提供接口對相應的定時器節點進行操作,具體代碼如下:

class TimerManager
{
private:
    TimerNode* head;
    map<int, TimerNode*> TimerNodes;
    int currentTimerNum;
    TimerNode* nextCheckNode;
    
    
public:
    TimerManager();
    ~TimerManager();
    int setTimer(TimerObject* obj, int interval, bool type=true, int total=0);
    void checkTimers(struct timeval& time);
    bool killTimer(int id);
    bool changeTimer(int id, int interval);
    
}

void checkTimers(struct timeval& time)
{
    //遍歷所有的定時器,並執行定時器相應的任務(DoSomething函數)

MilliTime current_time(tNow);

// 在這個函數里面有可能會調用KillTimer,SetTimer
TimerNode* current_node = used_head->next_node;

next_check_node = NULL ;
if( current_node != NULL)
{
next_check_node = current_node->next_node ; //下一個節點,有可能被kill掉
}

while( current_node != NULL)
{
int timer_id = current_node->time_id ;

// 到達了觸發次數
if(!current_node->loop_type && current_node->trigger_count == current_node->trigger_total )
{

}
// 沒到點
else if( !(current_node->start_time + current_node->interval_time < current_time))
{

}
else
{
// 觸發 :OnTimer有可能KillTimer,SetTimer,
current_node->timer_obj->OnTimer(timer_id,current_time);

// 該定時器id是否在OnTimer里面被kill掉了
if(TimerNodes.find(timer_id ) != TimerNodes.end())
{
current_node->start_time = current_time;
if(!current_node->loop_type)
{
current_node->trigger_count++;
}
}
}

current_node = next_check_node;
if(next_check_node)
{
next_check_node = next_check_node->next_node;
}
}

  

}

bool killTimer(int id)
{
    map<int TimerNode*>::iterator iter = TimerNodes.find(id);
    if (iter == TimerNodes.end())
    {
        return true;
    }
    TimerNode* delNode = iter->second;
    TimerNode* preNode = delNode->preTimerNode;
    TimerNode* nextNode = delNode->nextTimerNode;
    if (nextNode == NULL)
    {
        preNode->nextTimerNode = NULL;
    } else {
        preNode->nextTimerNode = nextNode;
        nextNode->preTimerNode = preNode;
    }
    
    TimerNodes.erase(iter);
    delete delNode;
    return true;
}

TimerManager()
{
    head = new TimerNode();
    int currentTimerNum = 0;
}

~TimerManager()
{
  TimerNode* p = null
  TimerNode* q = null
  p = head->nextTimerNode;
  while(p != null)
  {
    q = p
    p = p->nextTimerNode;
    delete q;
  }
  TimerNodes.clear();
}
int setTimer(TimerObject* obj, int interval, bool type, int total) { if(obj == NULL || total < 0 || (type == false && total == 0)) { return -1; } TimerNode* newNode = new TimerNode(); struct timeval now; getCurrentTime(&now); newNode->id = getTimerId(); newNode->startTime = Time(now); newNode->interval = interval; newNode->curCount = 0; newNode->totalCount = total; newNode->isForever = type; newNode->obj = obj; TimerNode* next = head->nextTimerNode; if (next) { head->nextTimerNode = newNode ; newNode->nextTimerNode = next ; newNode->preTimerNode = head ; next->preTimerNode = newNode ; } else { head->nextTimerNode = newNode; newNode->preTimerNode = head; } TimerNodes[newNode->id] = newNode; return newNode->id; } //取得一個空閑的id值 int getTimerId() {
  int id =
currentTimerNum++;
  if (id < 0)
  {
    currentTimerNum = 0;
    id = currentTimerNum++;
}
  map<int, TimerNode*>::iterator iter;
  while(true)
  {
    iter = TimerNodes.find(id);
    if (iter == TimerNodes.end())
    {
      return id;
    } else {
      id = currentTimerNum++;
      if (id < 0)
      {
        currentTimerNum = 0;
        id = currentTimerNum++;
      }
    }
  }
  
} void getCurrentTime(struct timeval* now) { #ifdef LINUX #ifdef IOS gettimeofday(now, NULL); #else struct timespec time_spec = {0, 0}; int get_spec = clock_gettime(CLOCK_MONOTONIC, &time_spec); if(get_spec == 0) { now->tv_sec = time_spec.tv_sec; now->tv_usec = time_spec.tv_nsec/1000; } else { now->tv_sec = 0 ; now->tv_usec = 0 ; } #endif #endif }

 

5.示例,展現如何使用定時器   

  一方面應構建一個繼承TimeObject的Task類型,並且實現基類中的虛函數。另一方面,在main函數中構建定時器管理對象,且啟動定時任務。最后,構建一個循環不斷的檢測所有的定時器,判斷是否有定時器觸發,若有觸發,則執行對應的任務函數。用例的代碼實現如下:

class Task : public TimeObject
{
private:
    int task_id;
    static TimerManager* timeMng;
    
public:
    Task()
    {
        task_id = -1;        
    }
    ~Task()
    {
        if (task_id != -1)
        {
            timeMng->killTimer(task_id);
        }
    }
    static void setTimeManager(TimerManager* timeM)
    {
        timeMng = timeM;
    }
    void start()
    {
        task_id = timeMng->setTimer(this, 10*1000, true, 0);
    }
    
    void DoSomething(int time_id, Time time_val)
    {
        if (task_id == time_id)
        {
            //執行定時任務
        }
    }
    
}

int main()
{
    TimerManager* timeMng = new TimerManager();
    Task* task = new Task();
    Task::setTimeManager(timeMng);
    task->start();
    
    while(1)
    {
        struct timeval now;
        getCurrentTime(&now);
        timeMng->checkTimers(now);
        sleep(1);
    }
    
    return 0;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM