高精度计时器QueryPerformanceCounter正确的打开方式(windows环境下)

来源:互联网 发布:windows phone 流畅 编辑:程序博客网 时间:2024/05/14 07:39

引言

游戏程序中有很多需要用到时间的地方,往往会通过windows API来获取时间。先前写过一篇文章是关于时间同步的:网络游戏中的(低精度)时间同步,当需求更高精度的时间同步时,就需要QueryPerformanceCounter这样的API,QueryPerformanceCounter的使用有一些隐含的陷阱需要注意。

关于QueryPerformanceCounter
官方解释:https://msdn.microsoft.com/zh-cn/ms644904,用于高精度计时器时间读取,重点是执行成功返回非0值,精度是<1us的。

多核CPU采用QueryPerformanceCounter的问题
程序中通常使用时间差定时地调用某种接口的情况,而时间差的使用有一个隐含因素,即时间是顺序累加的,当然我们通常的时间当然是累加的,不会出现停滞甚至倒转,而QueryPerformanceCounter的运行情况是依赖于CPU的,当CPU是多核时,在某一线程内调用QueryPerformanceCounter,线程会切换于不同的核心之间,这时候QueryPerformanceCounter返回值是不确定的,或者说这时候的计时器并不能保证是顺序累加的,相应地,当使用时间差时会出现负数或者0的情况,这显然不符合开发者的预期。

如何在多核CPU的环境下使用QueryPerformanceCounter
目前多核的CPU已经飞入寻常百姓人家,因而作为开发人员,不得不面对在多核CPU的机器上使用QueryPerformanceCounter的情况。当我们需要在某一进程中获取时间,需要将该线程绑定在某一固定的核心上,这样获取的高精度计时器才是可靠的。通过SetThreadAffinityMask可以实现这一目的。举个栗子:
HANDLE thread = GetCurrentThread();// Set affinity to the first coreDWORD_PTR oldMask = SetThreadAffinityMask(thread, GTimerMask );// Query the timerQueryPerformanceCounter(&mStartTime);mStartTick = GetTickCount();// Reset affinitySetThreadAffinityMask(thread, oldMask);
使用前,需要重置GTimerMask:
void appResetTimerMask(){// Get the current process core maskULONG_PTR proMask;ULONG_PTR sysMask;GetProcessAffinityMask(GetCurrentProcess(), &procMask, &sysMask);// If procMask is 0, consider there is only one core available// (using 0 as procMask will cause an infinite loop below)if (procMask == 0)     procMask = 1;// Find the lowest core that this process uses      if (GTimerMask == 0)      {          GTimeMask = 1;          while ((GTimerMask & procMask) == 0)          {              GTimerMask <<= 1;          }      }}
完整的使用方法:

inline DOUBLE appSeconds(){LARGE_INTEGER Cycles;HANDLE thread = NULL;ULONG_PTR oldMask = 0;if (GEnableTimerAffinityMask)      //GEnableTimerAffinityMask为TRUE时需要将线程绑定到固定核心{thread = GetCurrentThread();if (GTimerMask == 0)       //GTimerMask默认值为0{appResetTimerMask();}oldMask = SetThreadAffinityMask(thread, GTimerMask);}QueryPerformanceCounter(&Cycles);if (GEnableTimerAffinityMask){SetThreadAffinityMask(thread, oldMask);}return Cycles.QuadPart * GSecondsPerCycle + 16777216.0;}

参考资料:
http://bbs.csdn.net/topics/310177138
https://msdn.microsoft.com/en-us/library/ee417693%28v=vs.85%29.aspx
http://blog.csdn.net/hunter8777/article/details/6204719



1 0