C#TimeSpan.FromTicks()不准确?

时间:2011-10-18 15:10:34

标签: c# performance timespan stopwatch

我一直在使用C#做一个小游戏,而在另一台PC上测试游戏时,我遇到了一些奇怪的时间问题。

我已根据自上次游戏循环后的时间更新本游戏中设置的所有内容,在大多数情况下应该是这样,但在第二台PC上,所有内容都已关闭。

我发现问题与使用FromTicks()方法创建TimeSpan有关。我使用以下代码进行了一些测试:

class Program
{
    static void Main(string[] args)
    {
        System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
        sw.Start();
        System.Threading.Thread.Sleep(TimeSpan.FromSeconds(1));
        sw.Stop();
        TimeSpan t = TimeSpan.FromTicks(sw.ElapsedTicks);
        Console.WriteLine(t.ToString());
        Console.WriteLine(sw.Elapsed.ToString());
        Console.ReadKey();
    }
}

在我的主电脑上,我运行了这个程序并获得了以下内容:

    00:00:00.3528353
    00:00:00.9856987

我完全没想到的东西。我认为第二个结果非常不准确,但第一个结果很好。

然后我在另一台PC上运行了相同的程序并得到了这个:

    00:03:20.6866734
    00:00:00.998287

我非常震惊。

我的问题不是我如何解决这个问题,我已经决定使用第二种方法,因为它足够准确......相反,我要求启发。

怎么会这样?为什么第一个结果如此不准确?为什么这在另一台机器上变化很大?

我检查msdn以防我使用该方法时出错,但是那里的示例显示我的结果应该是不可能的......

注意:
我认为CMOS电池正在死亡/死亡,这是一个因素吗?

3 个答案:

答案 0 :(得分:6)

Timespan的Ticks和Stopwatch的Ticks是不同的。要进行协调,请使用Stopwatch Elapsed属性,该属性可根据机器的刻度正确转换为TimeSpan。

    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch(); 
    sw.Start();         
    System.Threading.Thread.Sleep(TimeSpan.FromSeconds(1));         
    sw.Stop();         

    // don't load a TimeSpan with ElapsedTicks, these have diff frequency,
    // call Elapsed instead to get TimeSpan.
    TimeSpan t = sw.Elapsed;         
    Console.WriteLine(t.ToString());         
    Console.ReadKey(); 

或者,您当然可以自己划分频率等,但这需要做更多的工作。主要的一点是你不应该把TimeSpan的Ticks和Stopwatch的Ticks一样考虑。

相关主题:What are timer ticks, the unit used by Stopwatch.ElapsedTicks

答案 1 :(得分:6)

摘要:秒表的频率在不同的硬件上可能会有所不同,这意味着刻度线(其间隔基于频率)具有不同的大小(并且与刻度的大小不同)时间跨度和日期时间对象)。

简而言之,请直接使用Elapsed属性:

    TimeSpan t = sw.Elapsed;

...或者如果您需要执行计算,请使用Ticks的{​​{1}}属性:

Elapsed

带引用的长版本:

http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.elapsedticks.aspx是经过时间刻度的msdn页面。值得注意的是:

  

此属性表示基础中经过的刻度数   计时器机制。刻度是最小的时间单位   秒表计时器可以测量。使用频率字段转换   ElapsedTicks值为几秒钟。

从频率字段的页面:

  

定时器频率表示定时器精度和分辨率。对于   例如,每秒200万个滴答的定时器频率等于a   定时器分辨率为每个刻度500纳秒。换句话说,因为   一秒等于10亿纳秒,定时器频率为2   每秒百万次刻度相当于每1百万次刻度   十亿纳秒,可以进一步简化为每500个1个刻度   毫微秒。

     

频率值取决于基础时间的分辨率   机制。如果安装的硬件和操作系统支持a   高分辨率性能计数器,然后频率值反映   那个柜台的频率。否则,频率值基于   关于系统定时器频率。

     

因为秒表频率取决于安装的硬件和   操作系统,频率值保持不变   系统正在运行。

所以秒表的频率基本上可以在不同的硬件上有所不同,这意味着刻度线的大小不同(与时间跨度和日期时间对象中的刻度不同)。

有趣的是,您已经在使用Elapsed属性StopWatch,它为您提供了一个时间跨度。 sw.Elapsed是一个TimeSpan,它可能是你在尝试获取TimeSpan对象时所追求的。如果你想使用刻度线,你可以使用这个TimeSpan的Ticks属性。

或者,您可以使用ElapsedMilliseconds返回long。

答案 2 :(得分:0)

仅为您提供信息......我测试了它,如果您绝对想要使用" FromTicks"这就是如何做到的。但如果我是你,我会跟随James Michael Hare的建议。

    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    sw.Start();
    System.Threading.Thread.Sleep(TimeSpan.FromSeconds(1));
    sw.Stop();

    // don't load a TimeSpan with ElapsedTicks, these have diff frequency,
    // call Elapsed instead to get TimeSpan.
    TimeSpan t1 = sw.Elapsed;
    Debug.Print("Millisecs: " + t1.TotalMilliseconds);


    // TimeSpan.Elapsed Code
    //if (!SafeNativeMethods.QueryPerformanceFrequency(out Stopwatch.Frequency))
    //{
    //  Stopwatch.IsHighResolution = false;
    //  Stopwatch.Frequency = 10000000L;
    //  Stopwatch.tickFrequency = 1.0;
    //}
    //else
    //{
    //  Stopwatch.IsHighResolution = true;
    //  Stopwatch.tickFrequency = 10000000.0;
    //  Stopwatch.tickFrequency /= (double)Stopwatch.Frequency;
    //}

    //public TimeSpan Elapsed
    //{
    //  [__DynamicallyInvokable]
    //  get
    //  {
    //      return new TimeSpan(this.GetElapsedDateTimeTicks());
    //  }
    //}

    //private long GetElapsedDateTimeTicks()
    //{
    //  long rawElapsedTicks = this.GetRawElapsedTicks();
    //  if (Stopwatch.IsHighResolution)
    //      return (long)((double)rawElapsedTicks * Stopwatch.tickFrequency);
    //  return rawElapsedTicks;
    //}

    TimeSpan t2;
    if (Stopwatch.IsHighResolution)
    {
        t2 = TimeSpan.FromTicks((long)((double)sw.ElapsedTicks * ((double)10000000.0 / (double)Stopwatch.Frequency)));
    }
    else
    {
        t2 = TimeSpan.FromTicks(sw.ElapsedTicks);
    }
    Debug.Assert(t1.TotalMilliseconds == t2.TotalMilliseconds); // true, 

    return;