.NET System.Diagnostics.Stopwatch问题(返回值太低)

时间:2011-02-25 04:51:30

标签: c# .net stopwatch

在我的电脑上,秒表的返回值太低了。例如,我指定Thread.Sleep(1000)时为200毫秒。该程序应该等待1秒钟。我还使用ManualResetEvent.WaitOne(1000)进行了测试并获得了相同的结果。框架2.0和3.0都给出了这种奇怪的行为。我正在运行带有.NET Framework 3.5 SP1的Windows XP SP3。

以下是我的测试结果(下面的代码):

1000 ms for DateTime.Now.Ticks
0201 ms for Stopwatch.ElapsedTicks
0142 ms for Stopwatch.ElapsedMilliseconds
0139 ms for Stopwatch.ElapsedTicks after Reset
0264 ms for Stopwatch.ElapsedTicks setting ThreadAffinity
0151 ms for Stopwatch.ElapsedTicks setting ProcessorAffinity (and more)
0371 ms for Stopwatch.ElapsedTicks with Syncronized object
Done!

// Program.cs file

class Program
{
    static void Main(string[] args)
    {
        StopWatchTest.Go();
        Console.WriteLine("Done!");
        Console.ReadLine();
    }
}

// StopWatchTest.cs类

internal static class StopWatchTest
{
    public const int SleepTime = 1000;

    public static void Go()
    {
        #region Test #0 with DateTime.Now.Ticks
        long startTick = DateTime.Now.Ticks;
        Thread.Sleep(SleepTime);
        long stopTick = DateTime.Now.Ticks;
        long elapsedDt = (stopTick - startTick) * 100;
        Display((int)(elapsedDt / 1000 / 1000), "DateTime.Now.Ticks");
        #endregion Test #0 with DateTime.Now.Ticks

        Stopwatch watch = Stopwatch.StartNew();
        long frequency = Stopwatch.Frequency;
        double nanosecPerTick = (1000.0 * 1000.0 * 1000.0) / frequency;

        #region Test #1 with Stopwatch.ElapsedTicks
        startTick = watch.ElapsedTicks;
        Thread.Sleep(SleepTime);
        stopTick = watch.ElapsedTicks;
        double elapsedSw = (stopTick - startTick) * nanosecPerTick;
        Display((int)(elapsedSw / 1000 / 1000), "Stopwatch.ElapsedTicks");
        #endregion Test #1 with Stopwatch.ElapsedTicks

        #region Test #2 with Stopwatch.ElapsedMilliseconds
        startTick = watch.ElapsedMilliseconds;
        Thread.Sleep(SleepTime);
        stopTick = watch.ElapsedMilliseconds;
        Display((int)(stopTick - startTick), "Stopwatch.ElapsedMilliseconds");
        #endregion Test #2 with Stopwatch.ElapsedMilliseconds

        #region Test #3 with Stopwatch.ElapsedTicks after Reset
        watch.Stop();
        watch.Reset();
        watch.Start();
        startTick = watch.ElapsedTicks;
        Thread.Sleep(SleepTime);
        stopTick = watch.ElapsedTicks;
        elapsedSw = (stopTick - startTick) * nanosecPerTick;
        Display((int)(elapsedSw / 1000 / 1000), "Stopwatch.ElapsedTicks after Reset");
        #endregion Test #3 with Stopwatch.ElapsedTicks after Reset

        #region Test #4 with Stopwatch.ElapsedTicks and ThreadAffinity
        Thread.BeginThreadAffinity();
        startTick = watch.ElapsedTicks;
        Thread.Sleep(SleepTime);
        stopTick = watch.ElapsedTicks;
        elapsedSw = (stopTick - startTick) * nanosecPerTick;
        Display((int)(elapsedSw / 1000 / 1000), "Stopwatch.ElapsedTicks setting ThreadAffinity");
        Thread.EndThreadAffinity();
        #endregion Test #4 with Stopwatch.ElapsedTicks and ThreadAffinity

        #region Test #5 with Stopwatch.ElapsedTicks and ProcessorAffinity (and more)
        const int affinity = 0x0001;
        Process proc = Process.GetCurrentProcess();
        proc.ProcessorAffinity = new IntPtr(affinity);
        proc.PriorityClass = ProcessPriorityClass.High;
        ProcessThreadCollection ptc = proc.Threads;
        foreach (ProcessThread pt in ptc)
        {
            pt.IdealProcessor = 0;
            pt.ProcessorAffinity = new IntPtr(affinity);
        }
        Thread.CurrentThread.Priority = ThreadPriority.Highest;

        startTick = watch.ElapsedTicks;
        Thread.Sleep(SleepTime);
        stopTick = watch.ElapsedTicks;
        elapsedSw = (stopTick - startTick) * nanosecPerTick;
        Display((int)(elapsedSw / 1000 / 1000), "Stopwatch.ElapsedTicks setting ProcessorAffinity (and more)");
        #endregion Test #5 with ProcessorAffinity and more

        #region Test #6 with Syncronized object
        elapsedSw = new SyncTimer().Go();
        Display((int)(elapsedSw / 1000 / 1000), "Stopwatch.ElapsedTicks with Syncronized object");
        #endregion Test #6 with Syncronized object
    }

    private static void Display(int milliseconds, string testName)
    {
        Console.WriteLine("{0:0000} ms for {1}", milliseconds, testName);
    }
}

[Synchronization]
internal class SyncTimer : ContextBoundObject
{
    [MethodImpl(MethodImplOptions.Synchronized)]
    public double Go()
    {
        Stopwatch.StartNew();
        long frequency = Stopwatch.Frequency;
        double nanosecPerTick = (1000.0 * 1000.0 * 1000.0) / frequency;

        long startTick = Stopwatch.GetTimestamp();
        Thread.Sleep(StopWatchTest.SleepTime);
        long stopTick = Stopwatch.GetTimestamp();
        return (stopTick - startTick) * nanosecPerTick;
    }
}

7 个答案:

答案 0 :(得分:6)

查看以下链接:

https://connect.microsoft.com/VisualStudio/feedback/details/94083/stopwatch-returns-negative-elapsed-time

System.Diagnostics.Stopwatch returns negative numbers in Elapsed... properties

看起来秒表的准确性存在问题,这会导致它返回负的经过时间,并且也会出现大幅变化的时间。如果你看一下Connect问题,那里的大多数人都在谈论在虚拟机上看到问题,这就是我们看到负面Elapsed值问题的地方。

查看QueryPerformanceCounter doc,似乎暗示由于BIOS或硬件抽象层错误,多处理器系统可能会发生此问题,但没有提供进一步的信息,也没有具体说明虚拟机。

至于解决这个问题的方法......我在google搜索中找不到一个。您可以忽略小于零的值,这在某些情况下并不理想但可行。但这并不能解决您的问题 - 您如何知道哪些值无效?

希望这有所帮助。

答案 1 :(得分:2)

我知道这是一个老问题,但我认为在遇到同样的问题后我会提供2美分:

我开始按照@AllonGuralnek的建议查看频率,它确实以秒为单位提供了准确的时间,但它也减少了我想要捕获的剩余毫秒数。

无论如何,经过大量的来回,并没有得到任何关键,我注意到sw.Elapsed有一个Ticks属性,这个给了我准确的蜱数,一旦转换回来它给了我一个准确的时间。

代码明智,这就是我最终的结果:

Stopwatch sw = new Stopwatch();
sw.Start();

... DO WORK

sw.Stop();

long elapsedTicks = sw.Elapsed.Ticks;
Debug.WriteLine(TimeSpan.FromTicks(elapsedTicks).ToString());

运行测试时,请致电:

  • sw.Elapsed.ToString():“00:00:11.6013029”

  • sw.ElapsedTicks:当调用TimeSpan.FromTicks(sw.ElapsedTicks).ToString()不准确时,返回“40692243”并转换为“00:00:04.0692243”。

  • sw.Elapsed.Ticks:在致电TimeSpan.FromTicks(sw.Elapsed.Ticks).ToString()时,返回“116013029”并转换为“00:00:11.6013029”。

虽然我可能会遗漏某些东西,但我觉得sw.ElaspedTicks返回的值不同于sw.Elapsed.Ticks是没有意义的,所以如果有人想对此有所了解,请做,但是从我的我认为这是一个错误,如果没有,至少它感觉非常不一致!。

注意:调用sw.ElapsedTicks / Stopwatch.Frequency会返回11(即秒),但正如我所说,它会减少毫秒,这对我没用。

答案 2 :(得分:1)

我明白了:

1000 ms for DateTime.Now.Ticks
0999 ms for Stopwatch.ElapsedTicks
1000 ms for Stopwatch.ElapsedMilliseconds
0999 ms for Stopwatch.ElapsedTicks after Reset
0999 ms for Stopwatch.ElapsedTicks setting ThreadAffinity
0999 ms for Stopwatch.ElapsedTicks setting ProcessorAffinity (and more)

(无法运行最后一次测试)

在Linqpad中使用.NET4的四核i7机器上。

我只倾向于使用Stopwatch.ElapsedMilliseconds,但我从来没有见过任何奇怪的事情。听起来好像你的机器或虚拟化平台已经崩溃了。

答案 3 :(得分:0)

在某些情况下,好像你正在使用Tick计数。请记住,默认情况下,在现代Windows上,操作系统将节省CPU。这意味着刻度计数和经过的时间不是的线性比例。

我建议您尝试使用最基本形式的Stopwatch.ElapsedMilliseconds

var sw = new Stopwatch();
sw.Start();
Thread.Sleep(1000);
var elpased = sw.Elapsed;

答案 4 :(得分:0)

如果秒表不起作用,您可以使用Windows上的QueryPerformanceCounter

http://www.windojitsu.com/code/hirestimer.cs.html

上查看这个小课程

答案 5 :(得分:0)

您可以使用该代码修复“Stopwatch.Elapsed”方法问题:

using System;
using System.Diagnostics;

namespace HQ.Util.General
{
    public static class StopWatchExtension
    {
        public static TimeSpan ToTimeSpan(this Stopwatch stopWatch)
        {
            return TimeSpan.FromTicks(stopWatch.ElapsedTicks);
        }
    }
}

用法:

using HQ.Util.General;

Debug.Print($"Elapsed millisec: { stopWatch.ToTimeSpan().TotalMilliseconds}");

答案 6 :(得分:-1)

我相信你会在这里找到答案: http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/eec73d6e-f597-4342-be48-5d2ce30202f0/

但是有关更多细节和说明,请参阅: http://msdn.microsoft.com/en-us/magazine/cc163996.aspx http://stackoverflow.com/questions/394020/stopwatch-accuracy http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.aspx