【问题标题】:System.Diagnostics.Stopwatch returns negative numbers in Elapsed... propertiesSystem.Diagnostics.Stopwatch 在 Elapsed... 属性中返回负数
【发布时间】:2010-11-03 17:56:07
【问题描述】:

秒表可以返回负值是正常行为吗?下面的代码示例可以用来重现它。

 while (true)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            sw.Stop();

            if (sw.ElapsedMilliseconds < 0)
                Debugger.Break();

        }

我可以重现负数的唯一地方是我的虚拟机(由 Hyper-V 在 8 核机器上托管)

【问题讨论】:

  • 负数时ElapsedMilliseconds的值是多少?
  • 卸载你的磁通电容器。
  • 它每次都在变化。出现负数可能需要多次循环。
  • 我要求更多的幅度。它通常在-10ms左右吗?还是通常是-4000000000ms?
  • Stopwatch 类在 VM 环境下也会随机返回极高的值。

标签: c# stopwatch


【解决方案1】:

这是bug。它似乎并没有引起太多关注,所以我建议跟进该报告。

uninspiring workaround 似乎是忽略负值:

long elapsedMilliseconds = Math.Max(0, stopwatch.ElapsedMilliseconds);

【讨论】:

  • 看来重现它的唯一方法是使用虚拟机。我已经在几台台式机上尝试过代码 - 没有成功
  • 此解决方法仅消除负值 - 它不会消除它还返回的极高(假)正值。
  • 它在 .NET 4 中工作,但很难相信该错误仍然存​​在于 .NET 3.5 中:(
  • 它在 .NET 4 中并没有完全修复;在 Connect 报告和我的回答中查看我的 cmets...
【解决方案2】:

我使用 Reflector 对 .NET 2.0 和 .NET 4.0 中的 Stopwatch 类进行了反编译,然后比较了差异以了解它是如何修复的。除了添加新的 Restart 方法之外,我发现的唯一不同之处在于:

public void Stop()
{
    if (this.isRunning)
    {
        long num2 = GetTimestamp() - this.startTimeStamp;
        this.elapsed += num2;
        this.isRunning = false;
// THE NEXT 4 LINES ARE NEW IN .NET 4.0:
        if (this.elapsed < 0L)
        {
            this.elapsed = 0L;
        }
    }
}

因此,如果值为负数,则基本上他们在 Stop 方法中将 elapsed 设置为 0。恕我直言,仍然存在错误:

  1. 如果用户在停止秒表之前读取了任何 Elapsed 属性怎么办?您可能仍会得到负值。
  2. 重置为 0 不正确。 必须经过一些时间,即使只有几微秒!
  3. 它无法处理我和其他人报告的异常大的正经过值。

编辑:不幸的是,#2 和 #3 超出了 .NET Framework 的能力:

这是问题的核心:来自 QueryPerformanceCounter 上的 MSDN,这是 Stopwatch 类使用的 API:

在多处理器计算机上,哪个处理器无关紧要 叫做。但是,你可以在不同的地方得到不同的结果 由于基本输入/输出系统 (BIOS) 或 硬件抽象层(HAL)。指定处理器亲缘关系 线程,使用 SetThreadAffinityMask 函数。

不要只使用 .NET 4.0 秒表并假设问题已解决。事实并非如此,除非您希望他们破坏您的线程亲和力,否则他们对此无能为力。来自 Stopwatch 类文档:

在多处理器计算机上,哪个处理器无关紧要 线程继续运行。但是,由于 BIOS 或硬件中的错误 抽象层(HAL),你可以得到不同的时序结果 不同的处理器。要指定线程的处理器亲和性,请使用 ProcessThread.ProcessorAffinity 方法。

【讨论】:

    【解决方案3】:

    Microsoft Connect Bug 上的“Closed as Fixed”解决方案一定意味着他们在 .NET 4 中修复了它。我已经在我的桌面和 VM 上运行了几分钟的重现代码,并且没有得到任何负值。

    【讨论】:

    • 它在 .NET 4 中并没有完全修复;在 Connect 报告和我的回答中查看我的 cmets...
    【解决方案4】:

    我发现在 VM 中获得正确运行时间的唯一解决方法是 (VB):

    Dim tstart AS DateTime = Now
    ...executing code...
    Dim telapsed = (Now - tstart).TotalMilliseconds
    

    【讨论】:

    • downvote 不是我...但可能是因为DateTime.NowTicks 属性与Stopwatch 的分辨率不同(据我所知,TicksDateTime.Now 每 15 毫秒更新一次)
    • 请注意,Now 属性将在夏令时转换期间大幅向前或向前跳跃。它也比UtcNow慢得多。
    • 这是完全错误的,永远不要使用系统时间来衡量代码执行时间,系统时间会因时间同步而改变,用户可以轻松更改。
    猜你喜欢
    • 2015-12-14
    • 2011-07-04
    • 1970-01-01
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    • 2010-09-28
    • 2018-01-22
    相关资源
    最近更新 更多