2015-11-05 24 views
1

我最近注意到.NET框架中的ManualResetEvent类非常奇怪的行为。我正在使用C#,VS 2015,项目的目标设置为4.5.2。下面是完整的代码:PC启动后C#.NET ManualResetEvent的奇怪行为

using System; 
using System.Diagnostics; 
using System.Threading; 
using System.Threading.Tasks; 

namespace CSharpCOnsole 
{ 
    class Program 
    { 
     private static ManualResetEvent exit = new ManualResetEvent(false); 

     static void Main(string[] args) 
     { 
      var t = Task.Factory.StartNew(F); 
      Console.ReadKey(); 
      exit.Set(); 
      t.Wait(); 
      exit.Close(); 
     } 

     static void F() 
     { 
      var dtStopwatch = new Stopwatch(); 
      uint ii = 0; 
      while (!exit.WaitOne(25)) { 
       dtStopwatch.Stop(); 
       var dt = 1000.0 * dtStopwatch.ElapsedTicks/Stopwatch.Frequency; 
       dtStopwatch.Restart(); 

       if (ii++ % 40 == 0) { 
        Console.WriteLine(dt.ToString("F3")); 
       } 
      } 
     } 
    } 
} 

我知道这听起来很蠢,但这里是发生了什么:如果我重新启动我的电脑,开机后VS向右运行,运行这个程序,我得到下面的输出:

31.665 
31.365 
31.541 
... 

更重要的是,如果我在范围16改变!exit.WaitOne(25)25任何其他号码31,我得到了相同的结果:它等待31毫秒。如果我选择115范围内的任何数字,它将精确等待16毫秒。等等,如果我选择3247范围内的任何标准,它就等于48毫秒。但是:如果我编译并运行这个代码几次(大约10-30)或者等待一段时间(大约5-20分钟后启动),它突然开始工作正常!是的,这听起来很讽刺,但它会发生什么。它以1 ms的精度开始阻塞精确给定时间的循环。它一直持续到下一台电脑重新启动。我在两台不同的PC上试过这个,并得到相同的行为。谷歌搜索没有给我这个问题绝对没有。如果我运行没有VS的编译EXE,我会得到相同的行为。

+0

听起来像它可能只是其启动并修改某些后台服务某些时点的时钟精度。 –

+0

“它突然开始工作正常”可能有一些其他程序提高了计时器分辨率。 Chrome做了一段时间。消耗电池。 – usr

+0

可能是处理器时间的时间片放置在16ms的时间间隔内,以便在启动时负载过重时在程序执行之间切换。一旦拥有更多的资源,它就能够切换到需要处理时间的程序/线程。您可以检查进程管理器(sysinternals)以查看系统或任务管理器上的负载以获取有关负载的信息。 (我认为编译不是重点,而是编译所需的时间)。一旦负载下降,请检查您是否得到正确的结果。 – Uwe

回答

-1

我已经找到了答案 - 您可以通过调用TimeBeginPeriodhttps://stackoverflow.com/a/15071477/1389883

设置系统计时器的分辨率或者您可以使用多媒体计时器API:https://stackoverflow.com/a/24843946/1389883

+0

这将全局设置定时器分辨率。除非你知道你在做什么,并且正在专用计算机上运行,​​否则请不要这样做。考虑[this](https://msdn.microsoft.com/en-us/library/windows/desktop/dd742877(v = vs.85).aspx),而不是如果你真的需要这种精度。 – lsedlacek

1

等待任何种类的WaitHandle等待至少等于作为指定的超时,但可能/可能更长。实际的超时取决于系统时钟以及系统的繁忙程度。如果你想做精确时间WaitHandles或使用它们的类(如ManualResetEvent)不是要走的路。

+0

那么什么建议阻止游戏循环? Thread.Sleep在PC启动后5-20分钟有相同的奇怪行为 – iamnp

+0

可以在一个循环中执行'Thread.Sleep(0)'或'Thread.Yield()',如下所示:http://stackoverflow.com/a/25497775/5401421 – lsedlacek