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

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

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

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

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

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

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

  1. unsigned long dwNowTime = GetTickCount();  
  2.   
  3.   
  4. // 计时器  
  5. if (dwNowTime-m_dwPrevTick >= 10)  
  6. {  
  7.     // 记录此次执行时间  
  8.     m_dwPrevTick = dwNowTime;  
  9.   
  10.   
  11.     map<unsigned short, TIMEINFO>::iterator it = m_mapTimeInfos.begin();  
  12.     for (; it!=m_mapTimeInfos.end(); ++it)  
  13.     {  
  14.         if (0 == it->second.uElapse)  
  15.         {  
  16.             continue;  
  17.         }  
  18.   
  19.         unsigned short nCount = (unsigned short)((dwNowTime-(it->second.dwTick))/(it->second.uElapse));  
  20.   
  21.         for (int i=0; i<nCount; ++i)  
  22.         {  
  23.             m_listTimerEvents.push_back(it->first);  
  24.             it->second.dwTick = dwNowTime;//记录此次执行时间  
  25.         }  
  26.     }  
  27. }  
  28.   
  29.   
  30. // 定时器:每一秒运行一次  
  31. if (dwNowTime-m_dwPrevTickD >= 500)  
  32. {  
  33.     m_dwPrevTickD = dwNowTime;  
  34.   
  35.     time_t t;  
  36.     time(&t);  
  37.     tm* pNow = localtime(&t);  
  38.   
  39.     map<unsigned short, TIMEINFOD>::iterator itD = m_mapTimerDInfos.begin();  
  40.     for (; itD!=m_mapTimerDInfos.end(); ++itD)  
  41.     {  
  42.         if (itD->second.stTm.tm_hour==pNow->tm_hour  
  43.         && itD->second.stTm.tm_min==pNow->tm_min  
  44.         && itD->second.stTm.tm_sec==pNow->tm_sec  
  45.         && itD->second.nExeSec!=pNow->tm_sec)  
  46.         {  
  47.             m_listTimerEvents.push_back(itD->first);  
  48.             itD->second.nExeSec = pNow->tm_sec;  
  49.         }  
  50.     }  
  51. }  
  52.   
  53.   
  54. // 计时器事件回调  
  55. bool bRun = false;  
  56. for (unsigned short i = 0; (m_listTimerEvents.size()>0)&&(i<nLimitedCount); ++i)  
  57. {  
  58.     unsigned short nIDEvent = m_listTimerEvents.front();  
  59.     m_listTimerEvents.pop_front();  
  60.   
  61.   
  62.     if (NULL != m_pITimer)  
  63.     {  
  64.         m_pITimer->OnTimer(nIDEvent);  
  65.         bRun = true;  
  66.     }  
  67. }  
  68.   
  69. // 实现的核心代码(单线程计时器):  
  70. char szBuffer[0xFFFF];//临时用  
  71.   
  72. while (true)  
  73. {  
  74.     unsigned long nRet = WaitForSingleObject(m_handleThread, TIMER_WAIT_SINGLE);  
  75.   
  76.   
  77.     if (WAIT_OBJECT_0 == nRet)  
  78.     {  
  79.         break;  //收到信号量,停止线程  
  80.     }  
  81.   
  82.   
  83.     if (m_bNeedUpdate)  
  84.     {  
  85.         // 更新计时器信息到计时器信息列表副本  
  86.         m_mapTempTimeInfos.clear();  
  87.         EnterCriticalSection(&m_crit);  
  88.         m_mapTempTimeInfos = m_mapTimeInfos;  
  89.         m_mapTempTimeDInfos = m_mapTimeDInfos;  
  90.         LeaveCriticalSection(&m_crit);  
  91.         m_bNeedUpdate = false;  
  92.     }  
  93.   
  94.   
  95.     // 执行计时器判定,并将计时器ID投递计时器触发队列  
  96.     unsigned long dwNowTime = GetTickCount();  
  97.     map<unsigned short, TIMEINFO>::iterator it = m_mapTempTimeInfos.begin();  
  98.     for (; it!=m_mapTempTimeInfos.end(); ++it)  
  99.     {  
  100.         if (0 == it->second.uElapse)  
  101.         {  
  102.         continue;  
  103.         }  
  104.     }  
  105.   
  106.     // 判断此次循环与上次循环的时间差,来计算需要触发多少次计时器  
  107.     unsigned short nCount = (unsigned short)((dwNowTime-(it->second.dwTick))/(it->second.uElapse));  
  108.     if (nCount > 0)  
  109.     {  
  110.         it->second.dwTick = dwNowTime;//记录此次执行时间  
  111.   
  112.         int nFreeLen = m_pipeEvents.GetFreeLen();//计时器触发队列空闲长度  
  113.         if (nFreeLen >= (int)(sizeof(unsigned short)*nCount))  
  114.         {  
  115.             // 将触发次数写入计时器触发队列  
  116.             char* pBuf = szBuffer;  
  117.             int nLen = 0;  
  118.             for (int i=0; i<nCount; ++i)  
  119.             {  
  120.                 *(unsigned short*)(pBuf+nLen) = it->first;  
  121.                 nLen += sizeof(unsigned short);  
  122.             }  
  123.             m_pipeEvents.WriteData(szBuffer, nLen);  
  124.         }  
  125.     }  
  126.   
  127.   
  128.   
  129.     // 定时器判定  
  130.     if (dwNowTime-m_dwPrevTickD >= 500)  
  131.     {  
  132.         m_dwPrevTickD = dwNowTime;  
  133.   
  134.         time_t t;  
  135.         time(&t);  
  136.         tm* pNow = localtime(&t);  
  137.   
  138.         map<unsigned short, TIMEINFOD>::iterator itD = m_mapTempTimeDInfos.begin();  
  139.         for (; itD!=m_mapTempTimeDInfos.end(); ++itD)  
  140.         {  
  141.             if (itD->second.stTm.tm_hour==pNow->tm_hour  
  142.             && itD->second.stTm.tm_min==pNow->tm_min  
  143.             && itD->second.stTm.tm_sec==pNow->tm_sec  
  144.             && itD->second.nExeSec!=pNow->tm_sec)  
  145.             {  
  146.                 // 触发定时器  
  147.                 char* pBuf = szBuffer;  
  148.                 int nLen = 0;  
  149.                 *(unsigned short*)(pBuf+nLen) = itD->first;  
  150.                 nLen += sizeof(unsigned short);  
  151.                 m_pipeEvents.WriteData(szBuffer, nLen);  
  152.                 itD->second.nExeSec = pNow->tm_sec;  
  153.             }  
  154.         }  
  155.     }  
  156. }  



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

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

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

Einzelheiten zur Compiler-Optimierung finden Sie in unserem Optimierungshinweis.