当GetTickCount()包装时会发生什么?

Sco*_*ham 14 c c++ windows winapi

如果一个线程正在做这样的事情:

const DWORD interval = 20000;
DWORD ticks = GetTickCount();
while(true)
{
    DoTasksThatTakeVariableTime();

    if( GetTickCount() - ticks > interval )
    {
        DoIntervalTasks();
        ticks = GetTickCount();
    }
}
Run Code Online (Sandbox Code Playgroud)

最终,当值不适合DWORD时,ticks将会换行.

我和同事一直在讨论这件事.我们中的一个人认为,当发生换行时,代码仍会表现得很"漂亮",因为减法操作也会换行.我们其他人认为它并不总是有效,特别是如果间隔很大.

谁是对的,为什么?

谢谢.

Jon*_*eet 13

来自文档:

经过的时间存储为DWORD值.因此,如果系统连续运行49.7天,时间将回绕到零.要避免此问题,请使用GetTickCount64.否则,在比较时间时检查溢出情况.

但是,DWORD没有签名 - 所以你应该没问题.0 - "非常大的数字"="小数字"(假设您没有激活溢出检查,当然).我之前的编辑建议你得到一个负数,但那是在我考虑到DWORD是无符号之前.

如果操作时间不到 49.7天,你仍然会遇到问题.这对你来说可能不是问题;)

测试的一种方法是将GetTickCount()方法存根,以便您可以在明确地进行包装的情况下编写单元测试.再说一次,如果你真的只是怀疑算术部分,你可以很容易地为它编写单元测试:)真的,这个数字来自系统时钟的事实几乎是不相关的,只要你知道它的行为.包装 - 以及文档中指定的内容.


Joe*_*ams 10

没有什么不好的事情发生,只要:

  • 你减去DWORDs,而不是先转换成其他类型.

  • 你想要花费的时间超过49.7天.

这是因为无符号算术溢出在C中定义良好,包装行为正是我们想要的.

DWORD t1, t2;
DWORD difference;

t1 = GetTickCount();
DoSomethingTimeConsuming();
t2 = GetTickCount();
Run Code Online (Sandbox Code Playgroud)

t2 - t1将产生正确的值,即使GetTickCount包裹.只是不要转换t2t1一些其他类型(例如intdouble)做减法之前.

如果编程语言将溢出视为错误,则这将不起作用.如果DoSomethingTimeConsuming()花费超过49.7天,它也将无法工作.不幸的是,你无法通过观察t2t1多少次GetTickCount缠绕来判断.


让我们从通常情况开始,没有环绕发挥作用:

t1 = 13487231
t2 = 13492843
Run Code Online (Sandbox Code Playgroud)

在这里,t2 - t1 = 5612这意味着操作大约需要五秒钟.

现在考虑一个需要很短时间的操作,但是在哪里GetTickCount做了回合:

t1 = 4294967173
t2 = 1111
Run Code Online (Sandbox Code Playgroud)

操作花了1234ms,但计时器缠绕,并且1111 - 4294967173是伪造的值-4294966062.我们将做什么?

那么,modulo 2 32,减法的结果也包围了:

(DWORD)-4294966062 == (DWORD)1234
Run Code Online (Sandbox Code Playgroud)

最后,考虑边缘情况,其中操作需要将近 2 32毫秒,但不完全:

t1 = 2339189280
t2 = 2339167207
Run Code Online (Sandbox Code Playgroud)

在这里,GetTickCount缠绕着,然后回到原来的位置.

现在t2 - t1产生伪造的价值4294945223.那是因为这是操作实际花费的时间!

一般来说:

(base + offset) - base ? offset mod 2^32
Run Code Online (Sandbox Code Playgroud)


bk1*_*k1e 7

如果要测试GetTickCount()换行时会发生什么,可以启用Application Verifier的TimeRollOver测试.

在软件开发生命周期中使用应用程序验证程序:

TimeRollOver强制GetTickCount和TimeGetTime API比平时更快地翻转.这允许应用程序更容易地测试它们对时间翻转的处理.