我使用 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。恕我直言,仍然存在错误:
- 如果用户在停止秒表之前读取了任何 Elapsed 属性怎么办?你可能仍然会得到一个负值。
- 重置为 0 是不正确的。必须经过一段时间,即使只有几微秒!
- 它无法处理我和其他人报告的异常大的正消逝值。
编辑: 不幸的是,#2 和#3 超出了 .NET Framework 的能力:
这是问题的核心:来自 MSDN 上的QueryPerformanceCounter,这是 Stopwatch 类使用的 API:
在多处理器计算机上,调用哪个处理器并不重要。但是,由于基本输入/输出系统 (BIOS) 或硬件抽象层 (HAL) 中的错误,您可能会在不同的处理器上获得不同的结果。要指定线程的处理器亲和性,请使用 SetThreadAffinityMask 函数。
不要只使用 .NET 4.0 秒表并假设问题已解决。事实并非如此,除非您希望他们破坏您的线程亲和力,否则他们对此无能为力。从秒表类文档:
在多处理器计算机上,线程运行在哪个处理器上并不重要。但是,由于 BIOS 或硬件抽象层 (HAL) 中的错误,您可以在不同的处理器上获得不同的计时结果。要为线程指定处理器关联,请使用 ProcessThread.ProcessorAffinity 方法。