基于windows的毫秒级C++定时器类的实现

本文的重点还是类的实现,获取本地时间的WINAPI是GetLocalTime(),程序中仅此一个与平台相关的系统函数。
定时器功能:用户指定时间间隔和回调函数,每隔一段时间判断是否经过设定间隔,若是,执行回调函数。定时器共两个类型,DelayCall仅用于一次调用,AddTimer用于循环调用。
类型定义:

/*  定时器 */
class Timer{
    friend class TimerMgr;
protected:
    TimerIdType nTimerId;
    unsigned short nInterval;
    CallBackType emCallBackType;
    vector<void *> vCallBack;   //用于传参,第一个元素是函数指针,之后是该函数的参数指针
    TimerType emTimerType;

    unsigned short  lastMillSec;        //上次计时的毫秒数
    inline unsigned short _getLastMillSec(){ return lastMillSec; }
    inline void _setLastMillSec(unsigned short lastMillSec_arg){ lastMillSec = lastMillSec_arg; }
    Timer(  TimerIdType nTimerId_arg,
            unsigned short nInterval_arg, 
            CallBackType emCallBackType_arg, 
            vector<void *>vCallBack_arg, 
            TimerType emTimerType_arg = AddTimer
            ) 
            :nTimerId(nTimerId_arg), 
             nInterval(nInterval_arg), 
             emCallBackType(emCallBackType_arg), 
             vCallBack(vCallBack_arg),
             emTimerType(emTimerType_arg)
             {
                 SYSTEMTIME sys;
                 GetLocalTime(&sys);
                 lastMillSec = sys.wMilliseconds;
            }
public:
    ~Timer(){}

    inline TimerIdType getTimeId(){ return nTimerId; }
    inline unsigned short getInterval(){ return nInterval; }
    inline TimerType getTimerType(){ return emTimerType; }
    pair<bool, bool> Update(){ return CallBack(emCallBackType, vCallBack); }

};



class TimerMgr{
protected:
    static TimerMgr * pTimerMgrInstance;

    typedef unsigned int BitArrayType;  //暂时最多能注册32个定时器
#define TimerMaxNum (sizeof(BitArrayType) << 3)

    list<Timer *>   l_TimerList;
    BitArrayType    i_BitArray;         //每位的0/1值表示TimerId的占用情况

    class { //以时间间隔为升序排序标准,设计为嵌套类防止命名污染
    public:
        inline bool operator()(Timer* pTimer1, Timer* pTimer2)
        {
            return pTimer1->getInterval() < pTimer2->getInterval();
        }
    }   SortByInterval;

    TimerMgr() :i_BitArray(0){}

    /*  获取空闲定时器ID   */
    TimerIdType _getIdleTimerId();
    unsigned short _getEachTimerInterval(unsigned short now, unsigned short last);
    inline list<Timer *>::iterator _closeTimerByIter(list<Timer *>::iterator iter){ return l_TimerList.erase(iter); }
public:
    ~TimerMgr();
    static TimerMgr * GetTimerMgrInstance();

    inline size_t getTimerNum(){ return l_TimerList.size(); }

    TimerIdType Register(unsigned short nInterval_arg,
                        CallBackType emCallBackType_arg,
                        vector<void *>vCallBack_arg,
                        TimerType emTimerType_arg = AddTimer
                        );
    void CloseTimerById(TimerIdType nTimerId);
    map<TimerIdType, pair<bool, bool> > Update();
};

Timer就是一个具体的定时器,里边的数据成员有定时器id、时间间隔、回调函数类型、回调函数(及参数)的指针、定时器类型和上次计时的毫秒数。回调函数的调用是通过将函数指针和参数指针传入到一个vector中,根据回调函数类型来决定调用方法。其中用到了自己写的另两个文件commonfunc.h和commonfunc.c。
设计一个TimerMgr来管理所有的Timer。使用单例模式保证程序中该类型只有一个对象。TimerMgr中的数据成员有包含所有注册的Timer指针的List、表示TimerId占用情况的BitArray和用于list排序的嵌套类型函数对象。

设计思想:一个程序中的唯一TimerMgr实例维护一个列表,管理所有注册的Timer。Timer每次注册在TimerMgr中时,都会在BitArray中从低到高找一个空闲位标记,该位的编号为此定时器的ID。TimerMgr在Update方法中遍历列表,若历经时间到达时间间隔,则执行回调函数。若是一次性的定时器,回调函数执行完即删除该定时器,删除后BitArray中的该位置为空闲。

TimerMgr的操作细节:

/*
 * 用于获取空闲的定时器ID。
 * 在定时器个数还没饱和的情况下,在32位二进制中找第一个为0的位,
 * 该位的下标号就是空闲的定时器ID。
 * 说起来找二进制第一个1/0这样的问题有比循环的时间复杂度更低的方法,
 * 但是要么运用的数学原理过于高深,要么使用汇编指令bsf、bsr之类的。
 */
TimerIdType TimerMgr::_getIdleTimerId()
{
    unsigned char i = 0;
    for (; i_BitArray & (1 << i); i++);
    return i;
}


/*
 * 用于注册一个定时器。
 * 跳过一些检查语句,首先会获取空闲TimerId,然后将该位置位。
 * 动态分配Timer对象,将其放入TimerList。
 * 最后对List排序,排序规则是按照时间间隔的升序排序。这样方便遍历的
 * 的时候优先运行时间间隔小的定时任务。排序时传入可调用对象
 * SortByInterval,它是TimerMgr的嵌套类对象,仅类内可见且不影响
 * 外界命名。
 */
TimerIdType TimerMgr::Register( unsigned short nInterval_arg,
                                CallBackType emCallBackType_arg,
                                vector<void *>vCallBack_arg,
                                TimerType emTimerType_arg /* = AddTimer */
                                )
{
    if (getTimerNum() >= TimerMaxNum){
        cout << "[TimerMgr] can't register timer, there is no idle timer id ." << endl;
        return _TIMER_INVALID_VAL;
    }

    if (emCallBackType_arg >= emCallBackNum){
        cout << "[TimerMgr] can't register timer, CallBackType is invalid." << endl;
        return _TIMER_INVALID_VAL;
    }

    if (emTimerType_arg >= TimerTypeNum){
        cout << "[TimerMgr] can't register timer, TimerType is invalid." << endl;
        return _TIMER_INVALID_VAL;
    }

    TimerIdType newId = _getIdleTimerId();
    i_BitArray |= (1 << newId); //置位
    Timer * pTimer = new Timer(newId, nInterval_arg, emCallBackType_arg, vCallBack_arg, emTimerType_arg);
    l_TimerList.push_back(pTimer);
    l_TimerList.sort(SortByInterval);   //按照时间间隔排序

    return newId;
}


/*
 * 若到达时间间隔,执行所有定时器的回调函数并更新上次运行时间。
 * 返回类型是定时器ID和函数运行结果。
 * 首先获取当前时间,遍历定时器列表,若有时间间隔到达回调标准的定时
 * 器任务则执行并将结果记录以便返回。然后判断定时器类型,若是一次性
 * 的则删除这个节点,iter指向下一个节点。若刚删的节点是尾节点,则
 * 跳出循环。否则更新定时器时间基准。
 */
map<TimerIdType, pair<bool, bool> > TimerMgr::Update()
{
    map<TimerIdType, pair<bool, bool> > m_CallBackResults;
    SYSTEMTIME sys;
    GetLocalTime(&sys);
    auto iter = l_TimerList.begin();
    for (; iter != l_TimerList.end(); iter++)
    {
        if (_getEachTimerInterval(sys.wMilliseconds, (*iter)->_getLastMillSec()) >= (*iter)->getInterval())
        {
            cout << (int)(*iter)->getTimeId() << '\t';

            m_CallBackResults[(*iter)->getTimeId()] = (*iter)->Update();
            if ((*iter)->getTimerType() == DelayCall)   //删掉一次性的定时任务
            {
                //CloseTimerById((*iter)->getTimeId()); //不能用这个
                iter = _closeTimerByIter(iter);
                if (iter == l_TimerList.end())          //刚才删除的节点为尾节点
                    break;
                continue;
            }
            (*iter)->_setLastMillSec(sys.wMilliseconds);        //更新时间基准
        }
    }
    return m_CallBackResults;
}



/*
 * 主函数测试用例。
 * 这个定时器类是主函数循环更新的,没写成异步驱动。
 */
int main(void)
{   
    vector<void *> v1, v2;
    v1.push_back(fun1);
    v2.push_back(fun2);

    TimerMgr::GetTimerMgrInstance()->Register(999, emCallBack0_void, v1, DelayCall);
    TimerMgr::GetTimerMgrInstance()->Register(500, emCallBack0_void, v2, DelayCall);
    TimerMgr::GetTimerMgrInstance()->Register(459, emCallBack0_void, v1, AddTimer);
    TimerMgr::GetTimerMgrInstance()->Register(550, emCallBack0_void, v2, DelayCall);
    TimerMgr::GetTimerMgrInstance()->Register(699, emCallBack0_void, v1, DelayCall);
    TimerMgr::GetTimerMgrInstance()->Register(300, emCallBack0_void, v2, AddTimer);

    while (true){
        TimerMgr::GetTimerMgrInstance()->Update();
    }

    system("pause");
    return 0;
}

这个定时器类可以放入中断处理函数,每隔几个时钟周期更新一次。
完整内容在github中。
https://github.com/castleKaoCK/C-Class—Millseconds-Timer.git

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值