2013-09-28 47 views
8

我搜索了這個問題,但沒有看到答案。如果它是重複的,我會很樂意關閉它。Thread.Sleep(1)需要超過1ms

我目前正在嘗試對一項技術進行一些性能評估,並且看到了一些相當令人難以置信的結果,所以我決定嘗試一些。在那我想嘗試看看秒錶班是否返回了我的預期。

Stopwatch sw = Stopwatch.StartNew(); 
Thread.Sleep(1); 
sw.Stop(); 
Console.WriteLine(sw.ElapsedMilliseconds); 

在這種情況下,我幾乎看到15ms的返回值。我理解DateTime的解決方案在那裏,但不應該Thread.Sleep(1)睡1ms線程?我正在使用的系統返回Stopwatch.IsHighResolution爲真,並且它在.NET 4中運行。

背景: 此代碼的完整和適當的形式旨在收集Aerospike db get請求上的一些數字。數據庫不在同一個盒子上。當我在中間查詢時打印出sw.ElapsedMilliseconds時,我看到的主要是亞毫秒響應,這聽起來有點可疑,考慮到我的Java等效代碼在大多數時間返回的可信5ms-15ms響應。 Java代碼使用了System.nanoTime()的區別。通過在我的C#代碼submilli響應我的意思是Console.WriteLine(sw.ElapsedMilliseconds)打印0.

+0

我認爲金額非常低,需要更多的感謝實際時間才能讓線程進入睡眠狀態並再次醒來。我建議你嘗試增加'sleep()'參數,看看參數和實際的睡眠時間是相等的。 – Mehraban

+0

我逐步將其提高到100左右,並且我仍然一直看到5到15毫秒的高。我瞭解它不是一個實時操作系統,但我沒有想到會看到這 – Rig

+0

我不會說它必然是重複的,但答案就在那裏。我應該刪除這個問題嗎? – Rig

回答

27

定時器其他比秒錶增加了時鐘中斷。在Windows上默認每秒打64次。或15.625毫秒。所以一個少於16的Thread.Sleep()參數不會給你延遲,你總會得到至少15.625的間隔。同樣,如果您閱讀Environment.TickCount或DateTime.Now等,並且等待少於,那麼您將讀取相同的值並認爲0毫秒已經過去。

總是使用秒錶進行小增量測量,它使用不同的頻率源。它的分辨率是可變的,它取決於主板上的芯片組。但是你可以依靠它比微秒更好。 Stopwatch.Frequency給你的速度。

時鐘中斷率可以被改變,你必須pinvoke timeBeginPeriod()。這可以讓你下降到一毫秒,實際上使Thread.Sleep(1)準確。最好不要這樣做,這是非常不友好的權力。

+0

那麼,總是有多媒體計時器 –

+0

對於其他人來到這個線程,我測試了使用Thread.Sleep(1)在linux/ubuntu下使用Mono C#進行驗證,睡眠線程精確爲1ms。 – JaredBroad

+2

@JaredBroad - 當然,這個問題是Windows特有的。 (這個答案給了你一個關於如何「修復」窗口行爲的線索 - 但是,它又是耗電的,所以對於大多數場景來說,這是不值得的。) – BrainSlugs83

0

Thread.Sleep(n)意味着阻止當前線程至少可以發生的次數(或線程量)在n毫秒內。時間片的長度在不同版本/類型的Windows和不同處理器上有所不同,通常爲15到30毫秒。這意味着線程幾乎可以保證阻塞超過n毫秒。你的線程在n毫秒後重新喚醒的可能性幾乎是不可能的。所以,Thread.Sleep對時間沒有意義。

+0

我實際上並沒有使用Thread.Sleep進行計時,而是驗證了我的極快響應時間,事實證明,這似乎是準確的。儘管我明白你的觀點。 – Rig

0

Thread.Sleep(1)會放置一個線程睡眠1ms,但通常無法獲得精確度。

但是,一旦線程進入睡眠狀態,內核將其放回就緒隊列需要時間,然後在線程將再次運行時將其置於運行狀態。

所有這些因素取決於平臺,CPU數量,其他進程的數量等。對於非RT操作系統,您不能得到任何保證。

+0

基於RT的設備在此提供不同的合約嗎?這是相關的,因爲我正在處理單獨的兼容RT的應用程序。 – Rig

1

是的,你已經發現是絕對正確的。

Thread.Sleep不保證在N毫秒釋放。它將確保SleepN毫秒之前不會釋放。

換言之,Thread.Sleep(1)意味着睡眠時間至少爲1毫秒。操作系統不會爲特定線程安排特定時間段的時間片。

該線程不會被安排由操作系統執行指定的時間。此方法將線程的狀態更改爲包含WaitSleepJoin。

Msdn

5

我期待Thread.Sleep無非是圍繞在Win32 API Sleep託管包裝了。衆所周知,該API函數的分辨率較低。文檔說這個:

此功能會導致線程放棄其時間片的剩餘部分,併成爲unrunnable基於dwMilliseconds的值的間隔。系統時鐘以固定速率「滴答」。如果dwMilliseconds小於系統時鐘的分辨率,則線程可能會睡眠時間小於指定的時間長度。如果dwMilliseconds大於一個小數點但小於兩個,則等待可以是一到兩個滴答聲之間的任何地方,依此類推。要提高睡眠間隔的準確性,請調用timeGetDevCaps函數以確定支持的最小定時器分辨率和timeBeginPeriod函數,以將定時器分辨率設置爲最小值。調用timeBeginPeriod時要小心,因爲頻繁的調用會顯着影響系統時鐘,系統電源使用情況和調度程序。如果您調用timeBeginPeriod,請在應用程序的早期一次調用它,並確保在應用程序的最後調用timeEndPeriod函數。

休眠間隔過後,線程就可以運行了。如果指定0毫秒,則線程將放棄其時間片的其餘部分,但保持就緒狀態。請注意,現成的線程不能保證立即運行。因此,直到睡眠間隔過去一段時間後,線程纔可能運行。有關更多信息,請參閱計劃優先級。

所以,你可以使用timeBeginPeriodtimeEndPeriod建議增加定時器分辨率,但這真的不建議。如果你真的需要短時間阻止,那麼我建議你找到更好的解決方案。例如多媒體計時器。