2015-11-05 63 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