C++ 当GetTickCount()包装时会发生什么?
如果线程正在执行以下操作:C++ 当GetTickCount()包装时会发生什么?,c++,c,windows,winapi,C++,C,Windows,Winapi,如果线程正在执行以下操作: const DWORD interval = 20000; DWORD ticks = GetTickCount(); while(true) { DoTasksThatTakeVariableTime(); if( GetTickCount() - ticks > interval ) { DoIntervalTasks(); ticks = GetTickCount(); } } 最后,当值
const DWORD interval = 20000;
DWORD ticks = GetTickCount();
while(true)
{
DoTasksThatTakeVariableTime();
if( GetTickCount() - ticks > interval )
{
DoIntervalTasks();
ticks = GetTickCount();
}
}
最后,当值不适合DWORD时,记号将自动换行
我一直在和一位同事讨论这个问题。我们中的一个人相信代码在进行换行时仍然会表现得“很好”,因为减法操作也会进行换行。我们中的另一个人认为它不会一直起作用,特别是当间隔时间很长的时候
谁是对的,为什么
谢谢。发件人:
经过的时间存储为DWORD
价值因此,时间将结束
如果系统运行,大约为零
连续49.7天。避
要解决此问题,请使用GetTickCount64。
否则,请检查溢出
比较时间时的条件
但是,德沃德没有签名,所以你应该没事。0-“非常大的数字”=“小的数字”(当然,假设您没有任何溢出检查处于活动状态)。我之前的编辑建议你得到一个负数,但那是在我考虑到DWORD没有签名之前
但是,如果手术只需要不到49.7天,你仍然会遇到问题。这可能不是您的问题;)
测试的一种方法是去掉
GetTickCount()
方法,这样您就可以在显式包装的地方编写单元测试。再说一次,如果你真的只是怀疑算术部分,你可以很容易地为此编写单元测试:)真的,只要你知道系统时钟包装时的行为,那么这个数字来自系统时钟的事实几乎是无关紧要的——这在文档中有详细说明。我最近遇到了这个问题。我正在处理的代码在很多地方使用了GetTickCount(),以确定程序是否在某个特定任务上花费了太多时间,如果是这样,它将暂停该任务,并重新安排它以供以后执行。如果GetTickCount()在一个度量周期内被包装,就会导致代码过早超时。这是一项持续运行的服务,因此每49天就会有一次轻微的打嗝
我通过编写一个计时器类修复了它,该类在内部使用了GetTickCount(),但在值包装和补偿时检测到了它。您肯定需要处理这个勾号包装问题 Linux内核使用以下技巧处理此类记号换行问题: #在(a,b)之后定义时间(长)(b)-(长)(a)<0)) 该思想被强制转换为无符号,以对其进行签名并比较其值,然后仅当| a-b |可以测试它时;)-我这里有一个简单的测试应用程序,它将启动一个应用程序,并在其中钩住
GetTickCount()
,以便您可以从测试应用程序的GUI控制它。我写这篇文章是因为在某些应用程序中清除GetTickCount()
调用并不是那么容易
TickShifter是免费的,可在以下位置获得:
我是在写一系列关于测试驱动开发的文章时写这篇文章的,这些文章使用了一些断章取义地使用GetTickCount()
的代码
如果您感兴趣,可以在此处找到文章:
但是,总而言之,您的代码将正常工作…如果您想测试当
GetTickCount()
包装时会发生什么,您可以启用应用程序验证程序的时间滚动测试
发件人:
TimeRollOver强制GetTickCount和TimeGetTime API以比通常更快的速度滚动。这使得应用程序能够更轻松地测试其对时间翻转的处理
我建议计算两个刻度之间的实际运行时间,而不是依赖编译器来为您处理:
const DWORD interval = 20000;
#define TICKS_DIFF(prev, cur) ((cur) >= (prev)) ? ((cur)-(prev)) : ((0xFFFFFFFF-(prev))+1+(cur))
DWORD ticks = GetTickCount();
while(true)
{
DoTasksThatTakeVariableTime();
DWORD curticks = GetTickCount();
if( TICKS_DIFF(ticks, curticks) > interval )
{
DoIntervalTasks();
ticks = GetTickCount();
}
}
这篇文章帮助了我,但我认为其中存在一个缺陷:
#define TICKS_DIFF(prev, cur) ((cur) >= (prev)) ? ((cur)-(prev)) : ((0xFFFFFFFF-(prev))+(cur))
当我在环绕点测试这个时,我发现它被关闭了1
对我起作用的是:
define TICKS_DIFF(prev, cur) ((cur) >= (prev)) ? ((cur)-(prev)) : ((0xFFFFFFFF-(prev))+(cur)+1)
只要:
- 您可以减去DWORD,而不是先转换为其他类型
- 没有任何东西需要超过49.7天的时间
DWORD t1, t2;
DWORD difference;
t1 = GetTickCount();
DoSomethingTimeConsuming();
t2 = GetTickCount();
t2-t1
将生成正确的值,即使GetTickCount
环绕。在进行减法之前,不要将t2
和t1
转换为其他类型(例如int
或double
)
如果编程语言将溢出视为错误,这将不起作用。如果DoSomethingTimeConsuming()
花费的时间超过49.7天,它也不起作用。不幸的是,仅仅通过查看t2
和t1
有多少次GetTickCount
就无法分辨
让我们从通常的情况开始,在这种情况下,并没有使用概括法:
t1 = 13487231
t2 = 13492843
这里,t2-t1=5612
,这意味着操作大约需要5秒钟
现在考虑一个花费很短时间的操作,但是<代码> GETICKCOUNTUCT/CONT>绕了一圈:
t1 = 4294967173
t2 = 1111
该操作耗时1234ms,但计时器已关闭,1111-4294967173
是-4294966062
的伪值。我们到底要做什么
好的,模232,减法的结果也环绕着:
(DWORD)-4294966062 == (DWORD)1234
最后,考虑一个操作需要近232毫秒但不完全的边缘情况:
t1 = 2339189280
t2 = 2339167207
在这里,GetTickCount
绕了一圈,回到了原来的位置
现在,t2-t1
产生的伪值为42945223
。那是因为这是手术实际花费的时间
一般而言:
(base + offset) - base ≡ offset mod 2^32
我知道这与11年后的Vista中的GetTickCount64()完全无关,但这里有一点帮助代码,我从永远以来都在我的tookit中使用,这是我以前使用过的
inline DWORD GetElapsed(DWORD from, DWORD to = ::GetTickCount())
{
if (from < to) //check for wrap around condition
return (to - from);
else
return ((0xFFFFFFFFL - from) + 1 + to);
}
担心的不是你的手术可能需要比49.7天更长的时间,而是滴答声
inline DWORD GetElapsed(DWORD from, DWORD to = ::GetTickCount())
{
if (from < to) //check for wrap around condition
return (to - from);
else
return ((0xFFFFFFFFL - from) + 1 + to);
}
DWORD start = ::GetTickCount();
// Some time later
DWORD elapsed = GetElapsed(start);