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电池快没电了,这是一个因素吗?
摘要:秒表的频率可以在不同的硬件上不同,这意味着滴答(其间隔基于频率)具有不同的大小(并且与timespan和datetime对象中的滴答不同)。
简而言之,直接使用Elapsed
属性:
TimeSpan t = sw.Elapsed;
…或使用Elapsed
的Ticks
属性,如果需要进行计算:
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;