定时器,线程和编译器的错误行为

我遇到了麻烦,找不到任何答案,因为我甚至不知道要search什么。 我有一个使用QueryPerformanceCounter的计时器类,从我的应用程序,我启动了第二个线程对象,它有自己的实例计时器,我只是有一个无限循环从计时器获取增量时间,并使用它来输出循环迭代次数第二。

我注意到它给了我很奇怪的值,所以我开始打印delta时间,发现它有时是0,所以我进入了返回delta时间的方法,并做了一些testing。 这是我的deltaTime()方法:

double MyTimer2::deltaTime() { LARGE_INTEGER timenow; QueryPerformanceCounter(&timenow); //std::cout << "timenow=" << (double)timenow.QuadPart << " currentticks=" << (double)m_currentTicks.QuadPart << std::endl; double m_deltaTime = (double)(timenow.QuadPart - m_currentTicks.QuadPart) /* 1000.0*/ / (double)m_frequency.QuadPart; m_currentTicks = timenow; if(m_deltaTime < 0.000001) return 0.0; return m_deltaTime; } 

所以,我把一个断点放在“return 0.0;”上 会发生什么事情,大部分时间都是这样,这是不正确的。 但是,如果我取消打印代码的注释并运行,我将永远不会停在断点上。 所以在理论上,我的打印代码正在使其正常工作,而如果我将其删除,则事情就会停止工作! 这怎么可能,为什么发生,我该如何解决? 我试过_ReadWriteBarrier()失败。

提前致谢!

编辑:我需要一个高分辨率的物理模拟计时器!

几代以前的处理器, QueryPerformanceCounter()会读取CPU的周期计数器(例如rdtsc )。 使用这种方法,连续读取的滴答数量永远不会为零。 分辨率等于CPU时钟速率,例如3 GHz。

现代处理器有两个特点,使得周期计数器无法用于定时。 首先,你有多个内核,每个内核有自己的循环计数器。 线程可以在内核之间迁移,如果您从两个不同的内核读取循环计数器,则差异将与时间流逝无关。 甚至可能是消极的。 其次,你有基于负载的动态时钟(无论是降频节能还是性能超频)。 英特尔分别称这些“SpeedStep”和“Turbo Boost”。 如果循环速率不固定,则无法将滴答转换为时间。

因此, QueryPerformanceCounter现在使用称为高性能事件计数器(HPET)的专用硬件,分辨率为几MHz。 重要的是,不管你拥有多少核心,只有一个,它不会动态改变速度。 但是,由于分辨率较低,因此现在可以在两次滴答之间读取两次,在这种情况下,您会将经过的时间报告为零。

在实践中,这不是一个问题。 如果您需要比HPET更精确的计时,那么通用计算机并不适合您。 时间在纳秒范围内将受到中断的严重影响。

这个区块的目的可能是什么?

 if(m_deltaTime < 0.000001) return 0.0; 

它没有任何价值,它只是简单的结果,告诉你时间为零时,实际上没有。

首先,你的计时器是错误的:它消耗你的CPU集中。 在单核机器上,它会减慢所有的系统。 如果你想创建一个计时器和目标Windows,你可以使用计时器功能 。

然后, deltaTime()函数返回的每个非负值都是有效的 。 虽然您不在实时操作系统托管,每个操作可以采取任意的时间。 一次迭代可能需要几十个周期的处理器刻度,或几十年。 没有人保证。

第三,关于实验结果。 看起来,如果上下文在两次连续的时间测量之间切换一次,您将得到大约0.016s值,如果不是,则得到0.016s 0.000001s值,该值为0s

如前所述,打印到控制台的操作相对较为繁琐,而且在启用它时,实际上始终会进行上下文切换。

编辑

虽然QueryPerformanceCounter似乎提供了很好的解决方案,它陷阱了你。 除非您在实时操作系统中工作,否则您永远不会得到实际的高分辨率计时器。