2011-02-25 34 views
11

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

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

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的文件

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; 
    } 
} 
+0

@ user633578,您的代码存在错误。您应该为每个测试使用一个秒表实例。 – acoolaum 2011-02-25 05:15:08

+4

您必须将机器带到BIOS医生处。 – 2011-02-25 06:21:33

+0

我没有看到任何有关重新使用相同的秒表实例的限制。顺便说一下,第一次测试不会产生有效的值。 – Ducharme 2011-02-25 07:01:57

回答

-1
+1

您提到的链接是关于计数器的分辨率准确度。我的问题不是关于班级提供的毫秒数超过预期,而是少了很多! 只有MSDN上的NOTE部分很有用,并且我使用Microsoft建议的ProcessThread.ProcessorAffinity进行了测试。 – Ducharme 2011-02-25 07:04:06

+0

我发现这篇文章http://support.microsoft.com/kb/896256并检查了我的电脑上的库版本。它是5.1.2600.6055,比修补程序(5.1.2600.3023)中的更新。我添加了注册表项[HKEY_LOCAL_MACHINE \ SYSTEM \ CurrentControlSet \ Control \ Session Manager \ Throttle]并用“PerfEnablePackageIdle”= dword:00000000和“PerfEnablePackageIdle”= dword:00000001(每次重新启动计算机后)再次运行我的测试。我得到了相同的结果... – Ducharme 2011-02-25 08:46:02

0

好像你正在使用蜱在某些情况下,指望。请记住,在现代Windows上默认情况下,操作系统将节省CPU。这意味着滴答计数和已过时不是成线性比例。

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

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

没有运气!它在我的测试中返回127毫秒。
** 1000毫秒DateTime.Now.Ticks
0127毫秒Stopwatch.Elapsed
完成!** Stopwatch sw = new Stopwatch(); sw.Start(); Thread.Sleep(1000); Display((int)sw.Elapsed.TotalMilliseconds, "Stopwatch.Elapsed"); Ducharme 2011-02-25 09:01:26

+3

你运行一个虚拟机?访客机的主机是否具有CPU性能,以便在控制面板上达到最佳性能? – Wernight 2011-02-25 09:02:34

+0

如果是,它返回的值与睡眠时间成正比吗? – Wernight 2011-02-25 09:08:03

0

如果秒表不工作,你可以在Windows上使用QueryPerformanceCounter

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

+0

同样的结果。在计算机上尽可能多地处理进程后,我会进行一些测试。也许其中一个正在改变系统时钟... – Ducharme 2011-02-25 09:58:08

+2

'Stopwatch'类是通过使用P/Invoke调用本机QueryPerformanceCounter函数来实现的,而不是使用'Stopwatch'类自己做,不应该使一个区别。 – 2013-04-17 09:06:57

+0

@Martin:我从来不知道为什么在那台机器上(这是通过复制文件和注册表设置调整公司的)秒表错误了一英里。 – Wernight 2013-04-26 08:17:19

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) 

与.NET4在Linqpad(无法运行最后一次测试)

在一个四核i7机。

我只是倾向于使用Stopwatch.ElapsedMilliseconds,但我从来没有看到任何奇怪的事情。这听起来像是有关于您的机器或虚拟化平台的问题。

+1

非常感谢。许多人的结果和你一样。下一步:弄清楚什么是坏的以及如何解决它... – Ducharme 2011-02-25 12:06:40

5

看一看下面的链接:

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

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

似乎有与秒表精度的问题,这可能会导致它返回负经过的时间,也似地不同时间。如果你看一下连接问题,那里的大多数人都在讨论在虚拟机上看到问题,这是我们看到负面Elapsed值问题的地方。

看着QueryPerformanceCounter doc,这似乎意味着这个问题可能发生在多处理器系统上,这是由于BIOS或硬件抽象层错误,但没有提供进一步的信息,也没有针对虚拟化机器。

至于解决这个问题......我还没有找到一个在很多谷歌搜索。您可以忽略小于零的值,这在某些情况下不理想但可行。但是这并不能解决你的问题 - 你怎么知道什么值是无效的?

希望这有些帮助。

1

我知道这样一个老问题,但我认为同样的问题挣扎后,我会提供我的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(即秒),但正如我所说的,它会减少对我毫无用处的毫秒。