System.Diagnostics.Stopwatch在Elapsed ...属性中返回负数

时间:2009-06-17 17:00:16

标签: c# stopwatch

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

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

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

        }

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

4 个答案:

答案 0 :(得分:17)

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

uninspiring workaround似乎是忽略负值:

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

答案 1 :(得分:11)

我使用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方法中设置为0。还有bug恕我直言:

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

    以下是问题的核心:来自QueryPerformanceCounter上的MSDN,这是秒表类使用的API:

      

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

    不要只使用.NET 4.0秒表并假设问题已修复。它不是,除非你希望它们与你的线程亲和力混淆,否则它们无法做任何事情。来自Stopwatch类文档:

      

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

答案 2 :(得分:0)

Microsoft Connect Bug上的“Closed as Fixed”分辨率必须意味着他们在.NET 4中修复了它。我在桌面和VM上运行了repro代码几分钟并且没有负值。

答案 3 :(得分:-1)

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

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