【问题标题】:Windows timer sometimes runs twice as fastWindows 计时器有时会以两倍的速度运行
【发布时间】:2016-04-08 09:43:46
【问题描述】:

我在我的游戏中使用了一个计时器,它在一个单例中启动,并且每毫秒都有一个回调。但是,有时它的运行速度是原来的两倍。真的很少见,我只见过 2 到 3 次(10.000+ 次构建,数千次执行)。

这是(重要的)代码:

//local variable in the .cpp:
static float milliseconds=0;

//the "actual" variable used in the game
long int MilliSecondTimer::milliseconds=0;

void CALLBACK TimerProc(UINT wTimerID, UINT msg, DWORD dwUser, DWORD dw1, DWORD dw2)
{
    milliseconds++;
    MilliSecondTimer::GetInstance()->SetTime(int(milliseconds), int(guimseconds));
}

MilliSecondTimer::MilliSecondTimer()
{
    UINT TimerID = timeSetEvent(1, 0, TimerProc, 0, TIME_PERIODIC | TIME_CALLBACK_FUNCTION);
}

是不是这样如果单例的创建时间超过1毫秒,那么就会被调用两次,因为定时器函数会调用

MilliSecondTimer::GetInstance()->SetTime(...)

?

谢谢!

【问题讨论】:

标签: c++ windows timer


【解决方案1】:

这很难重现,因为您说它很少发生。但我相信您最初对问题原因的想法可能是正确的。在 timeSetEvent 的 MSDN 文档中它说:

多媒体定时器在自己的线程中运行

这意味着,正如你所说,如果单例的创建时间超过 1 毫秒,那么在这种情况下,单例的构造函数将被调用两次。对构造函数的第二次调用可能来自第一个计时器回调函数中的MilliSecondTimer::GetInstance()->SetTime(...) 调用,因为此时尚未创建单例,MilliSecondTimer::GetInstance() 将再次调用构造函数,进而创建另一个计时器对象。由于每个计时器都在自己的线程中运行,因此您现在将有两个计时器处理(递增)相同的数据(静态浮点毫秒),这解释了为什么它的运行速度是原来的两倍。如果是这种情况,那么由于在进程中创建了两个单例实例,但单例实例指针最终指向其中一个,因此也可能导致内存泄漏。

【讨论】:

  • 多媒体计时器不会“在线程中运行”。它运行在一个时间关键的,并且来自定时器中断。返回时,线程消失。另外,我认为不会有内存泄漏。
  • @Valmond 我认为文档试图说的是 lpTimeProc 指向的回调函数,该函数在周期性事件到期时调用,将在与开始的线程不同的线程中调用(即运行)指定的计时器事件(在本例中为单例构造函数 MilliSecondTimer::MilliSecondTimer())。这就是为什么我认为你会在两个线程中有两个回调函数,增加你的静态浮点毫秒变量,使它看起来运行速度是原来的两倍。
  • @Valmond 关于内存泄漏,如果我们假设问题是单例对象的构造花费超过 1 毫秒,那么您的第一个回调函数将导致再次调用单例类的构造函数我假设 MilliSecondTimer::GetInstance() 函数将尝试动态创建单例对象并将结果分配给该类的实例指针数据成员。在这种情况下,您有一个指针,但动态创建了单例类的两个对象。无论如何,这假设单例类的构建时间超过 1 毫秒。
  • 很公平 :-) 我已经更改了代码(首先是构造,然后才调用启动计时器的函数)所以希望这种情况不会再次发生(或者我会回来;-)
猜你喜欢
  • 2015-09-11
  • 1970-01-01
  • 1970-01-01
  • 2017-07-16
  • 1970-01-01
  • 2022-01-14
  • 2016-08-13
  • 2014-05-22
  • 1970-01-01
相关资源
最近更新 更多