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

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

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

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

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


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

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


	unsigned long dwNowTime = GetTickCount();


	// 计时器
	if (dwNowTime-m_dwPrevTick >= 10)
	{
		// 记录此次执行时间
		m_dwPrevTick = dwNowTime;


		map::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; ifirst);
				it->second.dwTick = dwNowTime;//记录此次执行时间
			}
		}
	}


	// 定时器:每一秒运行一次
	if (dwNowTime-m_dwPrevTickD >= 500)
	{
		m_dwPrevTickD = dwNowTime;

		time_t t;
		time(&t);
		tm* pNow = localtime(&t);

		map::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)&&(iOnTimer(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::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; ifirst;
					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::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%。如果有什么疑惑或者有更好的实现方法,望与本人联系([email protected]),大家一块探讨学习。


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

你可能感兴趣的:(C/C++,网络游戏服务器开发)