您的位置:首页 > 其它

实现了一个简单的timer

2009-04-01 22:59 190 查看
对于游戏编程来说,timer是绝对不陌生的,不过以前一直使用一些库提供的timer,像sdl的timer,windows api提供的timer,这段时间突然觉得像这种东西还是在自己的游戏引擎里面实现一个,作为基本类库使用。

首先参考了一下linux的timer机制,详细见 http://www.linuxforum.net/forum/showthreaded.php?Cat=&Board=driver&Number=385224&page=0&view=collapsed&sb=5&o=all&vc=1

不过我实现的跟这个还是有点不一样的。

主要思想就是采用一个timer wheel,假设有一个timer的循环队列,队列上面的每个对象(我们叫做slot)是一个timer的list,现在我们处理到了第m轮第n个slot上面,这时候注册了一个timer,这个timer的间隔为interval毫秒,那么这个timer就应该放到第 n + interval / timer_interval (timer_interval为定时器的精度时间)的slot上面,然后挂在到该slot对应的list末尾,因为我们的环有长度,所以如果注册的timer interval时间很长,interval / timer_interval + n可能超出了wheel的长度,不过由于我们是一个环状结构,所以我们这个timer会在第 m + interval % timer_interval轮之后运行,同时是放到 (n + interval / timer_interval) % max_slotnum这个slot上面。



注册完成以后,当我们的处理到相应的slot的上面的时候,我们会一次的从list上面取出timer,然后现判断是否该timer是在该轮运行,不是则不处理,是则处理。如果碰到interval为0的timer,则会一直处理,知道该timer自己删除自己。

另外,由于我们的定时器有一个最小精度,所以除了interval为0的以外,其他的小于timer_interval的时间,我们全认为是timer_interval。



这个timer实现的非常简单,而且只能保证精度为timer_interval(一般也就是30ms),不过对于我来说,已经够了。



下面帖代码。

/**********************************************/
//BaseType.h
/**********************************************/
#pragma once
typedef char                        int8;
typedef short                       int16;
typedef int                         int32;
typedef long long                   int64;
typedef unsigned char               uint8;
typedef unsigned short              uint16;
typedef unsigned int                uint32;
typedef unsigned long long          uint64;
/**********************************************/
//CTimer.h
/**********************************************/
#pragma once 
#include "BaseType.h"
#include <list>
class CTimer
{
    friend class CTimerMgr;
public:
    CTimer();
    ~CTimer();
    virtual void OnTimer();
private:
    uint32 m_uInterval;         //timer的间隔时间
    uint32 m_uRing;             //第几轮运行
    std::list<CTimer*>*  m_pTimerList;    //在当前哪个mgr list上面
    std::list<CTimer*>::iterator m_itInList;  //在timer list上面对应的iteratoro
    
};
/**********************************************/
//CTimer.cpp
/**********************************************/
#include "CTimer.h"
#include <list>
using namespace std;
CTimer::CTimer()
    : m_pTimerList(NULL),
      m_uInterval(0),
      m_uRing(0)
{
    
}
CTimer::~CTimer()
{
    if(m_pTimerList)
        m_pTimerList->erase(m_itInList);
}
void CTimer::OnTimer()
{
}

/**********************************************/
//CTimerMgr.h
/**********************************************/
#pragma once 
#include "BaseType.h"
#include <vector>
#include <list>
class CTimer;
class CTimerMgr
{
    friend class CTimer;
public:
    CTimerMgr(uint32 uInterval, uint32 uSlotNum);
    ~CTimerMgr();
    void RegisterTimer(CTimer* pTimer, uint32 uInterval);
    void UnRegisterTimer(CTimer* pTimer);
    void OnTimer();

private:
    std::vector<std::list<CTimer*>* >    m_vecTimerContainer;
    uint32  m_uInterval;
    uint32  m_uSlotNum;
    uint32  m_uRing;
    uint32  m_uCurSlot;
};
/**********************************************/
//CTimerMgr.cpp
/**********************************************/
#include "CTimerMgr.h"
#include "CTimer.h"
#include <vector>
#include <list>
#include <assert.h>

using namespace std;
CTimerMgr::CTimerMgr(uint32 uInterval, uint32 uSlotNum)
    : m_uInterval(uInterval),
      m_uSlotNum(uSlotNum),
      m_uRing(0),
      m_uCurSlot(0)
{
     for(int i = 0; i < uSlotNum; i++)
    {
        list<CTimer*>* pTimerList = new list<CTimer*>;
        m_vecTimerContainer.push_back(pTimerList);
    }
}
CTimerMgr::~CTimerMgr()
{
    for(int i = 0; i < m_uSlotNum; i++)
    {
        list<CTimer*>* pTimerList = m_vecTimerContainer[i];
        while(pTimerList->empty())
        {
            CTimer* pTimer = pTimerList->front();
            delete pTimer;
        }
        delete pTimerList;
    }
    m_vecTimerContainer.clear();
}
void CTimerMgr::RegisterTimer(CTimer* pTimer, uint32 uInterval)
{
    //除了0以外,timer的最小精度为m_uInterval
    if(uInterval != 0 && uInterval < m_uInterval)
        uInterval = m_uInterval;
    uint32 uSlot = m_uCurSlot + uInterval / m_uInterval;
    
    if(uSlot >= m_uSlotNum)
    {
        pTimer->m_uRing = m_uRing + uSlot / m_uSlotNum;
        uSlot = uSlot % m_uSlotNum;
    }
    else
    {
        pTimer->m_uRing = m_uRing;
    }
    
    list<CTimer*>* pTimerList = m_vecTimerContainer[uSlot];
    pTimer->m_pTimerList = pTimerList;
    pTimer->m_uInterval = uInterval;
    pTimerList->push_back(pTimer);
    pTimer->m_itInList = --pTimerList->end();
}
void CTimerMgr::UnRegisterTimer(CTimer* pTimer)
{
    if(!pTimer)
        return;
    list<CTimer*>* pTimerList = pTimer->m_pTimerList;
    pTimerList->erase(pTimer->m_itInList);
    pTimer->m_pTimerList = NULL;
    delete pTimer;
}
void CTimerMgr::OnTimer()
{
    list<CTimer*>* pTimerList = m_vecTimerContainer[m_uCurSlot];
    list<CTimer*> NextTimerList;
    while(!pTimerList->empty())
    {
        CTimer* pTimer = pTimerList->front();
        
        if(pTimer->m_uRing != m_uRing)
        {
            NextTimerList.push_back(pTimer);
            pTimerList->pop_front();
            continue;
        }
        if(pTimer->m_uInterval == 0)
        {
            //不停调用,直到删除自己
            while(!pTimer->m_pTimerList)
            {
                pTimer->OnTimer();
            }
            continue;
        }
        else
        {
            pTimer->OnTimer();
            //如果timer删除了
            if(!pTimer->m_pTimerList)
                continue;
            uint32 uSlot = pTimer->m_uInterval / m_uInterval;
            assert(uSlot > 0);
            uSlot += m_uCurSlot;
    
            if(uSlot >= m_uSlotNum)
            {
                pTimer->m_uRing = m_uRing + uSlot / m_uSlotNum;
                uSlot = uSlot % m_uSlotNum;
            }
            else
            {
                pTimer->m_uRing = m_uRing;
            }
            list<CTimer*>* pNewTimerList = m_vecTimerContainer[uSlot];
            pTimerList->pop_front();
            pNewTimerList->push_back(pTimer);
            pTimer->m_pTimerList = pNewTimerList;
            pTimer->m_itInList = --pNewTimerList->end();
        }
    }
    pTimerList->swap(NextTimerList);
    
    //移动到下一个slot,如果到了最后,则开始下一轮
    ++m_uCurSlot;
    if(m_uCurSlot == m_uSlotNum)
    {
        m_uCurSlot = 0;
        ++m_uRing;
    }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: