游戏服务器之Timer计时器(定时器)动态链接库【超联网】

来源:互联网 发布:淘宝买家秀裸露店铺 编辑:程序博客网 时间:2024/05/18 06:21

对于游戏服务器而言,计时器(每个多长时间触发一次)和定时器(定点触发)是不可或缺的一个组件。而计时器和定时器的做法又有很多种,我大致将其分成3中:

1,非线程实现计时器和定时器;

2,单线程实现计时器和定时器;

3,多线程实现计时器和定时器;


三种计时器的实现基本原理大致相同,运用时间戳GetTickCount()比较,Sleep等待,WaitForSingleObject等待。

本人为了使用方便,将此计时器进行了DLL封装。只对上面两种计时器进行了封装,本人不看好多线程计时器,所以没有做。因为计时器的cup消耗很低,而多线程计时器启动的多个线程的之间的线程切换会消耗很多cup。


unsigned long dwNowTime = GetTickCount();// 计时器if (dwNowTime-m_dwPrevTick >= 10){// 记录此次执行时间m_dwPrevTick = dwNowTime;map<unsigned short, TIMEINFO>::iterator it = m_mapTimeInfos.begin();for (; it!=m_mapTimeInfos.end(); ++it){if (0 == it->second.uElapse){continue;}unsigned short nCount = (unsigned short)((dwNowTime-(it->second.dwTick))/(it->second.uElapse));for (int i=0; i<nCount; ++i){m_listTimerEvents.push_back(it->first);it->second.dwTick = dwNowTime;//记录此次执行时间}}}// 定时器:每一秒运行一次if (dwNowTime-m_dwPrevTickD >= 500){m_dwPrevTickD = dwNowTime;time_t t;time(&t);tm* pNow = localtime(&t);map<unsigned short, TIMEINFOD>::iterator itD = m_mapTimerDInfos.begin();for (; itD!=m_mapTimerDInfos.end(); ++itD){if (itD->second.stTm.tm_hour==pNow->tm_hour&& itD->second.stTm.tm_min==pNow->tm_min&& itD->second.stTm.tm_sec==pNow->tm_sec&& itD->second.nExeSec!=pNow->tm_sec){m_listTimerEvents.push_back(itD->first);itD->second.nExeSec = pNow->tm_sec;}}}// 计时器事件回调bool bRun = false;for (unsigned short i = 0; (m_listTimerEvents.size()>0)&&(i<nLimitedCount); ++i){unsigned short nIDEvent = m_listTimerEvents.front();m_listTimerEvents.pop_front();if (NULL != m_pITimer){m_pITimer->OnTimer(nIDEvent);bRun = true;}}// 实现的核心代码(单线程计时器):char szBuffer[0xFFFF];//临时用while (true){unsigned long nRet = WaitForSingleObject(m_handleThread, TIMER_WAIT_SINGLE);if (WAIT_OBJECT_0 == nRet){break;  //收到信号量,停止线程}if (m_bNeedUpdate){// 更新计时器信息到计时器信息列表副本m_mapTempTimeInfos.clear();EnterCriticalSection(&m_crit);m_mapTempTimeInfos = m_mapTimeInfos;m_mapTempTimeDInfos = m_mapTimeDInfos;LeaveCriticalSection(&m_crit);m_bNeedUpdate = false;}// 执行计时器判定,并将计时器ID投递计时器触发队列unsigned long dwNowTime = GetTickCount();map<unsigned short, TIMEINFO>::iterator it = m_mapTempTimeInfos.begin();for (; it!=m_mapTempTimeInfos.end(); ++it){if (0 == it->second.uElapse){continue;}}// 判断此次循环与上次循环的时间差,来计算需要触发多少次计时器unsigned short nCount = (unsigned short)((dwNowTime-(it->second.dwTick))/(it->second.uElapse));if (nCount > 0){it->second.dwTick = dwNowTime;//记录此次执行时间int nFreeLen = m_pipeEvents.GetFreeLen();//计时器触发队列空闲长度if (nFreeLen >= (int)(sizeof(unsigned short)*nCount)){// 将触发次数写入计时器触发队列char* pBuf = szBuffer;int nLen = 0;for (int i=0; i<nCount; ++i){*(unsigned short*)(pBuf+nLen) = it->first;nLen += sizeof(unsigned short);}m_pipeEvents.WriteData(szBuffer, nLen);}}// 定时器判定if (dwNowTime-m_dwPrevTickD >= 500){m_dwPrevTickD = dwNowTime;time_t t;time(&t);tm* pNow = localtime(&t);map<unsigned short, TIMEINFOD>::iterator itD = m_mapTempTimeDInfos.begin();for (; itD!=m_mapTempTimeDInfos.end(); ++itD){if (itD->second.stTm.tm_hour==pNow->tm_hour&& itD->second.stTm.tm_min==pNow->tm_min&& itD->second.stTm.tm_sec==pNow->tm_sec&& itD->second.nExeSec!=pNow->tm_sec){// 触发定时器char* pBuf = szBuffer;int nLen = 0;*(unsigned short*)(pBuf+nLen) = itD->first;nLen += sizeof(unsigned short);m_pipeEvents.WriteData(szBuffer, nLen);itD->second.nExeSec = pNow->tm_sec;}}}}



因为公司的保密性,故没有将dll实现的全部源代码给出,相信有些经验的程序员就能自己动手做出来(毕竟最好用的还是自己写的)。在压缩包中已经包含编译后的dll和测试程序(vs2008开发)。

此链接库的效率非常高,测试期间观察cup使用,基本上都是0%,偶尔出现一次1%或2%。如果有什么疑惑或者有更好的实现方法,望与本人联系(hzdiy@126.com),大家一块探讨学习。


源代码程序可以在本人上传资源中找到。http://download.csdn.net/detail/hzdiy/4159100

原创粉丝点击