6

一段时间以来,我一直在使用 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();
    }
}

在我的主 PC 上,我运行了这个程序并得到了以下结果:

    00:00:00.3528353
    00:00:00.9856987

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

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

    00:03:20.6866734
    00:00:00.998287

我相当震惊。

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

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

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

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

4

3 回答 3

6

Timespan 的刻度和秒表的刻度是不同的。要进行协调,请改用 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 视为相同。

相关主题:什么是计时器滴答,Stopwatch.ElapsedTicks 使用的单位

于 2011-10-18T15:17:08.923 回答
6

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

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

    TimeSpan t = sw.Elapsed;

...或使用 的Ticks属性Elapsed,如果您需要执行计算:

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

带参考的长版:

http://msdn.microsoft.com/en-us/library/system.diagnostics.stopwatch.elapsedticks.aspx是您的已用刻度的 msdn 页面。值得注意的是:

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

从频率字段的页面:

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

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

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

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

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

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

于 2011-10-18T15:17:58.077 回答
1

仅供参考...我对其进行了测试,如果您绝对想使用“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;
于 2016-11-19T06:02:30.977 回答