C# TimeSpan.FromTicks() inaccurate?

本文关键字:inaccurate TimeSpan FromTicks | 更新日期: 2023-09-27 18:11:49

我已经使用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.352835300:00:00.9856987之前

我完全没有想到的事情。我认为第二个结果很不准确,但第一个结果很好。

然后我在另一台电脑上运行同样的程序,得到这个:

<>之前00:03:20.686673400:00:00.998287之前

我很吃惊。

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

怎么会这样呢?为什么第一个结果如此不准确?为什么这在不同的机器上差异很大?

我检查了msdn,以防我使用了错误的方法,但那里的例子表明,我的结果应该是不可能的…

注意:
我认为CMOS电池快没电了,这是一个因素吗?

C# TimeSpan.FromTicks() inaccurate?

摘要:秒表的频率可以在不同的硬件上不同,这意味着滴答(其间隔基于频率)具有不同的大小(并且与timespan和datetime对象中的滴答不同)。

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

    TimeSpan t = sw.Elapsed;

…或使用ElapsedTicks属性,如果需要进行计算:

    TimeSpan t = TimeSpan.FromTicks(2*sw.Elapsed.Ticks);

带引用的长版本:

http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.elapsedticks.aspx是您经过的时间的MSDN页面。需要注意的是:

此属性表示底层中经过的滴答数定时器机制。滴答是地球上最小的时间单位秒表计时器可以测量。使用Frequency字段来转换ElapsedTicks值转换为秒数。

从Frequency字段的那一页:

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

Frequency值取决于底层时序的分辨率机制。如果安装的硬件和操作系统支持a高分辨率性能计数器,则频率值反映计数器的频率。否则,"Frequency"为"based"关于系统定时器频率。

因为Stopwatch的频率取决于所安装的硬件和操作系统中,频率值保持不变,而

系统正在运行。

所以基本上秒表的频率在不同的硬件上是不同的,这意味着刻度的大小是不同的(并且与timespan和datetime对象中的刻度的大小不同)。

有趣的是,你已经使用了Elapsed属性StopWatch,它给了你一个时间跨度。西南。Elapsed是一个TimeSpan它可能是你在尝试获取TimeSpan对象时所追求的。如果你想使用ticks,你可以使用这个TimeSpan的ticks属性。

或者你可以使用ElapsedMilliseconds返回一个长。

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是一样的。

相关线程:秒表使用的单位是什么?ElapsedTicks

仅供参考…我对它进行了测试,如果你绝对想使用"FromTicks",这是如何做到的。但如果我是你,我会听从詹姆斯·迈克尔·黑尔的建议。

    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;