如果一个线程正在做这样的事情:
const DWORD interval = 20000; DWORD ticks = GetTickCount(); while(true) { DoTasksThatTakeVariableTime(); if( GetTickCount() – ticks > interval ) { DoIntervalTasks(); ticks = GetTickCount(); } }
最后,当值不适合DWORD时,将会包装刻度。
我一直在和一位同事讨论这个问题。 我们中的一个人认为,当包装发生时,代码仍然会performance得很好,因为减法 *** 作也会包装。 我们另一个人认为,这并不总是奏效,特别是如果时间间隔很大的话。
谁是对的,为什么?
“errno”声明的编译时错误
无法使用fopen()在windows 7的C驱动器中创build文件
DeBUGActiveProcess崩溃
linux不允许我访问一个固定的内存区域
什么是轻量级windows应用程序的最佳开源示例?
谢谢。
将用户在文本框中input的URL转换为URI格式
在linux中获取文件的最后修改时间
检测使用C#更改的活动窗口而不进行轮询
如何在CheckedBoxList控件中的项目上绘制string?
为什么我的dev-c ++“无法打开输出文件”?
从文档 :
所用时间以DWORD值存储。 因此,如果系统连续运行49.7天,时间将会回到零。 为了避免这个问题,使用GetTickCount64。 否则,在比较时间时检查溢出情况。
但是,DWORD是无符号的 – 所以你应该没问题。 0 – “非常大的数字”=“小数字”(假设您没有任何溢出检查活动,当然)。 我有一个以前的编辑,建议你会得到一个负数,但是之前我考虑到DWORD是无符号的。
如果 *** 作时间短于 49.7天,您仍然会遇到问题。 这对你来说可能不是问题;)
一种测试方法是将GetTickCount()方法存根出来,这样你就可以在明确地进行换行的地方编写单元测试。 然后,如果你真的只怀疑算术部分,你可以很容易地编写单元测试:)真的,这个数字来自系统时钟的事实是非常不相关的,只要你知道它的行为包装 – 这是在文档中指定的。
如果要测试GetTickCount()封装时发生的情况,可以启用Application VerifIEr的TimeRollover测试。
在软件开发周期内使用Application VerifIEr生命周期 :
TimeRollover强制GetTickCount和TimeGetTime API比平时快速翻转。 这允许应用程序更容易地测试其处理时间翻转。
没什么不好的事情发生,只要:
您减去DWORD s,而不是先转换为其他类型。
没有你想要的时间需要超过49.7天。
这是因为C语言中的无符号算术溢出是很好定义的,并且包装行为完全是我们想要的。
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 ,这意味着 *** 作需要大约五秒钟。
现在考虑一个需要很短时间的 *** 作,但是GetTickCount包装的地方:
t1 = 4294967173 t2 = 1111
该 *** 作耗时1234ms,但定时器缠绕,而1111 – 4294967173是1111 – 4294967173的虚假值。 我们会做什么?
那么,模2 2 32 ,减法的结果也包括在内:
(DWORD)-4294966062 == (DWORD)1234
最后,考虑一个 *** 作需要接近 2 32毫秒的边界情况,但不是这样:
t1 = 2339189280 t2 = 2339167207
在这里, GetTickCount缠住了,然后回到原来的位置。
现在t2 – t1产生4294945223的虚假值。 那是因为这是 *** 作实际花费的时间!
一般来说:
(base + offset) – base ≡ offset mod 2^32
我建议计算两个滴答之间的实际流逝时间,而不是依靠编译器来为你处理:
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(); } }
我最近遇到了这个问题。 我正在使用的代码在一堆地方使用GetTickCount()来确定程序是否花费了太多的时间在一个特定的任务,如果是的话,它会超时该任务,并重新安排它以备后用。 如果GetTickCount()在一个测量周期内被封装,将会导致代码过早超时。 这是一个不断运行的服务,每四十九天就会有一个小小的呃逆。
我通过编写一个内部使用GetTickCount()的定时器类来解决这个问题,但是当它的值被包装和补偿时,它会被检测到。
大爆炸发生。 哦,对不起,大爆炸包装。
你可以测试它;) – 我有一个简单的测试应用程序,它将启动一个应用程序并挂钩GetTickCount() ,以便您可以从测试应用程序的GUI控制它。 我把它写成了在某些应用程序中调用GetTickCount()调用并不那么容易。
TickShifter是免费的,可在这里: http : //www.lenholgate.com/blog/2006/04/tickshifter-v02.HTML
我写了一个关于测试驱动开发的系列文章,它使用了一些使用GetTickCount()代码。
如果您有兴趣,可以在这里找到相关文章: http : //www.lenholgate.com/blog/2004/05/practical-testing.HTML
但是,总之,你的代码将工作…
这篇文章帮了我,但我认为有一个错误:
#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)
当然,你需要处理这个刻度线问题。
linux内核使用以下技巧处理这种刻度线问题:
(a,b)((long)(b) – (long)(a)<0))#define time_after
这个想法是无符号的签名和比较它们的值,那么只有当| ab | <2 ^ 30时,这个包裹才不影响结果。
你可以尝试一下这个技巧,并了解它为什么会起作用。
由于DWORD也是无符号整数,这个技巧也应该适用于windows。
所以你的代码可能是这样的:
const DWORD interval = 20000;
DWORD ticks = GetTickCount()+ interval;
while(true){
DoTasksThatTakeVariableTime(); if(time_after(ticks,GetTickCount()) { DoIntervalTasks(); ticks = GetTickCount() + interval; }
}
只有在间隔小于0x2 ^ 30时才有效。
总结
以上是内存溢出为你收集整理的GetTickCount()包装时会发生什么?全部内容,希望文章能够帮你解决GetTickCount()包装时会发生什么?所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
请登录后查看评论内容