本文的重点还是类的实现,获取本地时间的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