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

Vad*_*iak 20 c# stopwatch

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

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

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

        }
Run Code Online (Sandbox Code Playgroud)

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

Mic*_*ren 17

这是一个错误.它似乎并没有引起很多关注,所以我建议跟进那份报告.

平凡的解决办法似乎是忽略负值:

long elapsedMilliseconds = Math.Max(0, stopwatch.ElapsedMilliseconds);
Run Code Online (Sandbox Code Playgroud)

  • 这种解决方法只能消除负值 - 它不会消除它返回的极高(错误)正值. (5认同)

Jam*_*ton 12

我使用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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

所以基本上,如果值为负,它们会在Stop方法中设置为0.还有bug恕我直言:

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

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

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

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

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

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