windows 当 GetTickCount() 换行时会发生什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/727918/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What happens when GetTickCount() wraps?
提问by Scott Langham
If a thread is doing something like this:
如果一个线程正在做这样的事情:
const DWORD interval = 20000;
DWORD ticks = GetTickCount();
while(true)
{
DoTasksThatTakeVariableTime();
if( GetTickCount() - ticks > interval )
{
DoIntervalTasks();
ticks = GetTickCount();
}
}
Eventually, ticks is going to wrap when the value doesn't fit in a DWORD.
最终,当值不适合 DWORD 时,滴答将换行。
I've been discussing this with a colleague. One of us believes the code will still behave 'nicely' when the wrap occurs, because the subtraction operation will also wrap. The other of us, believes it won't always work, especially if the interval is large.
我一直在和一位同事讨论这个问题。我们中的一个人认为,当发生换行时,代码仍然会表现得“很好”,因为减法运算也会换行。我们中的另一个人认为它并不总是有效,特别是如果间隔很大。
Who's right, and why?
谁是对的,为什么?
Thanks.
谢谢。
采纳答案by Jon Skeet
From the docs:
从文档:
The elapsed time is stored as a DWORD value. Therefore, the time will wrap around to zero if the system is run continuously for 49.7 days. To avoid this problem, use GetTickCount64. Otherwise, check for an overflow condition when comparing times.
已用时间存储为 DWORD 值。因此,如果系统连续运行 49.7 天,时间将回零。要避免此问题,请使用 GetTickCount64。否则,在比较时间时检查溢出情况。
However, DWORD is unsigned - so you should be okay. 0 - "very big number" = "small number" (assuming you don't have any overflow checking active, of course). I had a previous edit which suggested you'd get a negative number, but that was before I took into account that DWORD is unsigned.
但是,DWORD 是未签名的 - 所以你应该没问题。0 - “非常大的数字” = “小数字”(当然,假设您没有任何溢出检查活动)。我之前有一个编辑建议你得到一个负数,但那是在我考虑到 DWORD 是未签名之前。
You'll still have a problem if the operation takes just under49.7 days though. That may not be an issue for you ;)
但是,如果操作时间不到49.7 天,您仍然会遇到问题。这对你来说可能不是问题;)
One way to test would be to stub out the GetTickCount()
method so you could write unit tests where you explicitly make it wrap. Then again, if you're really only doubting the arithmetic part, you can easily write unit tests for that :) Really, the fact that the number is coming from a system clock is pretty much irrelevant so long as you know the behaviour when it wraps - and that's specified in the documentation.
一种测试方法是将GetTickCount()
方法存根,这样您就可以编写单元测试,显式地将其包装起来。再说一次,如果你真的只是怀疑算术部分,你可以很容易地为此编写单元测试:) 真的,只要你知道它的行为,数字来自系统时钟的事实几乎无关紧要包装 - 这在文档中指定。
回答by Joey Adams
Nothing bad happens, as long as:
只要:
You subtract
DWORD
s, rather than converting to some other type first.Nothing you're trying to time takes longer than 49.7 days.
您减去
DWORD
s,而不是先转换为其他类型。您想要计时的时间不会超过 49.7 天。
This is because unsigned arithmetic overflow is well-defined in C, and wrapping behavior does exactly what we want.
这是因为无符号算术溢出在 C 中有明确定义,包装行为正是我们想要的。
DWORD t1, t2;
DWORD difference;
t1 = GetTickCount();
DoSomethingTimeConsuming();
t2 = GetTickCount();
t2 - t1
will produce the correct the value, even if GetTickCount
wraps around. Just don't convert t2
and t1
to some other type (e.g. int
or double
) before doing the subtraction.
t2 - t1
将产生正确的值,即使GetTickCount
环绕。在做减法之前不要将t2
and转换t1
为其他类型(例如int
或double
)。
This won't work if the programming language treats overflow as an error. It also won't work if DoSomethingTimeConsuming()
takes longer than 49.7 days. You can't tell just by looking at t2
and t1
how many times GetTickCount
wrapped around, unfortunately.
如果编程语言将溢出视为错误,这将不起作用。如果DoSomethingTimeConsuming()
超过 49.7 天,它也将不起作用。不幸的是,您无法仅通过观察t2
和缠绕的t1
次数来判断GetTickCount
。
Let's start with the the usual case, where no wraparound comes into play:
让我们从通常的情况开始,在这种情况下,没有任何环绕作用:
t1 = 13487231
t2 = 13492843
Here, t2 - t1 = 5612
, which means the operation took about five seconds.
这里,t2 - t1 = 5612
,这意味着操作大约需要五秒钟。
Now consider an operation that takes a short amount of time, but where GetTickCount
did wrap around:
现在考虑一个需要很短时间的操作,但在哪里GetTickCount
回绕:
t1 = 4294967173
t2 = 1111
The operation took 1234ms, but the timer wrapped around, and 1111 - 4294967173
is the bogus value of -4294966062
. What ever will we do?
操作耗时 1234 毫秒,但计时器环绕,并且1111 - 4294967173
是-4294966062
. 我们会做什么?
Well, modulo 232, the result of subtraction wraps around, too:
好吧,模 2 32,减法的结果也环绕:
(DWORD)-4294966062 == (DWORD)1234
Finally, consider the edge case where an operation takes nearly232milliseconds, but not quite:
最后,考虑一个操作需要将近2 32毫秒的边缘情况,但不完全是:
t1 = 2339189280
t2 = 2339167207
Here, GetTickCount
wrapped around, and came right back around where it was.
在这里,GetTickCount
绕了一圈,又回到原来的地方。
Now t2 - t1
yields the bogus-looking value of 4294945223
. That's because that's the amount of time the operation actually took!
现在t2 - t1
产生 的假值4294945223
。那是因为这是操作实际花费的时间!
In general:
一般来说:
(base + offset) - base ≡ offset mod 2^32
回答by bk1e
If you want to test what happens when GetTickCount()
wraps, you could enable Application Verifier's TimeRollOver test.
如果您想测试GetTickCount()
换行时会发生什么,您可以启用 Application Verifier 的 TimeRollOver 测试。
From Using Application Verifier Within Your Software Development Lifecycle:
TimeRollOverforces the GetTickCount and TimeGetTime APIs to roll over faster than they normally would. This allows applications to test their handling of time rollover more easily.
TimeRollOver强制 GetTickCount 和 TimeGetTime API 以比通常更快的速度滚动。这允许应用程序更轻松地测试它们对时间翻转的处理。
回答by Remy Lebeau
I would suggest calculating the actual elapsed period between the two ticks, not relying on the compiler to handle it for you:
我建议计算两个滴答之间的实际经过时间,而不是依赖编译器来为您处理:
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();
}
}
回答by Ferruccio
I ran across that problem recently. The code I was working on used GetTickCount() in a bunch of places to determine if the program was spending too much time on a particular task and if so it would time-out that task and re-schedule it for later execution. What would happen is that if the GetTickCount() wrapped during one of the measurement periods, it would cause the code to time-out prematurely. This was a service which runs constantly, so every 49 days, it would have a slight hiccup.
我最近遇到了这个问题。我正在处理的代码在很多地方使用了 GetTickCount() 来确定程序是否在特定任务上花费了太多时间,如果是,它会超时该任务并重新安排它以供以后执行。会发生的情况是,如果 GetTickCount() 在测量周期之一内包装,则会导致代码过早超时。这是一项持续运行的服务,因此每 49 天,它就会出现轻微的故障。
I fixed it by writing a timer class which used GetTickCount() internally but detected when the value wrapped and compensated for it.
我通过编写一个计时器类来修复它,该类在内部使用 GetTickCount() 但在值包装和补偿时检测到它。
回答by Sam Liao
Surely You need to handle this tick wrap problem.
当然你需要处理这个刻度线问题。
Linux kernel handles such tick wrap problem with following trick:
Linux 内核通过以下技巧处理此类刻度换行问题:
#define time_after(a,b) ((long)(b) - (long)(a) < 0))
#define time_after(a,b) ((long)(b) - (long)(a) < 0))
The idea is cast unsigned to signed and compare their value, then only if the |a-b|<2^30, then the wrap does not influence the result.
这个想法是将无符号转换为有符号并比较它们的值,然后只有当 |ab|<2^30 时,换行才不会影响结果。
You can have a try with this trick and get learn why it works.
您可以尝试使用此技巧并了解其工作原理。
Since DWORD is also unsigned int, this trick should also works for windows.
由于 DWORD 也是 unsigned int,所以这个技巧也适用于 Windows。
So you code could be sth like:
所以你的代码可能是这样的:
const DWORD interval = 20000;
DWORD ticks = GetTickCount() + interval;
while(true) {
DoTasksThatTakeVariableTime(); if(time_after(ticks, GetTickCount()) { DoIntervalTasks(); ticks = GetTickCount() + interval; }
}
const DWORD 间隔 = 20000;
DWORD 滴答数 = GetTickCount() + 间隔;
而(真){
DoTasksThatTakeVariableTime(); if(time_after(ticks, GetTickCount()) { DoIntervalTasks(); ticks = GetTickCount() + interval; }
}
Only if interval less than 0x2^30, it works.
只有当间隔小于 0x2^30 时,它才有效。
回答by Len Holgate
You could test it ;) - I have a simple test application here which will launch an app and hook GetTickCount()
in it so that you can control it from the GUI of the test app. I wrote it as stubbing out the GetTickCount()
calls in some apps isn't that easy.
你可以测试它;) - 我在这里有一个简单的测试应用程序,它将启动一个应用程序并挂GetTickCount()
在它上面,这样你就可以从测试应用程序的 GUI 控制它。我写它是因为GetTickCount()
在某些应用程序中消除调用并不是那么容易。
TickShifter is free and is available here: http://www.lenholgate.com/blog/2006/04/tickshifter-v02.html
TickShifter 是免费的,可在此处获得:http: //www.lenholgate.com/blog/2006/04/tickshifter-v02.html
I wrote it whilst writing a series of articles on Test Driven Development which used some code that used GetTickCount()
in a broken way.
我在写一系列关于测试驱动开发的文章时写了它,其中使用了一些GetTickCount()
以破坏方式使用的代码。
Articles are available here in case you're interested: http://www.lenholgate.com/blog/2004/05/practical-testing.html
如果您有兴趣,可以在此处获取文章:http: //www.lenholgate.com/blog/2004/05/practical-testing.html
However, in summary, your code will work...
但是,总而言之,您的代码将起作用......
回答by botismarius
A big bang occurs. Oh, sorry, a big bang wraps.
发生大爆炸。哦,对不起,大爆炸包装。
回答by Dr.C
This article helped me, but I think there is a bug in:
这篇文章帮助了我,但我认为有一个错误:
#define TICKS_DIFF(prev, cur) ((cur) >= (prev)) ? ((cur)-(prev)) : ((0xFFFFFFFF-(prev))+(cur))
When I tested this at the wrap around point, I found it was off by 1.
当我在环绕点测试时,我发现它相差 1。
What worked for me was:
对我有用的是:
define TICKS_DIFF(prev, cur) ((cur) >= (prev)) ? ((cur)-(prev)) : ((0xFFFFFFFF-(prev))+(cur)+1)
回答by InsanityPants
I know this is completely irrelevant almost 11 years later and with the inclusion of GetTickCount64() from Vista onwards, but here is a little bit of helper code I've had in my tookit since forever which I used to use back when this was an issue.
我知道这在几乎 11 年后完全无关紧要,并且从 Vista 开始包含 GetTickCount64(),但这里有一些帮助代码我一直在我的 takeit 中使用,当这是一个问题。
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);
}
Usage
用法
DWORD start = ::GetTickCount();
// Some time later
DWORD elapsed = GetElapsed(start);
The worry wasn't that your operation might take longer that 49.7 days, but that the tick counter might roll over during your operation, thus making your elapsed time calculation unreliable.
担心的不是您的操作可能需要超过 49.7 天的时间,而是滴答计数器可能会在您的操作期间滚动,从而使您的经过时间计算不可靠。
Of course, all irrelevant now because of GetTickCount64()
当然,现在一切都无关紧要,因为 GetTickCount64()