上回話說我們觀察到在不同的機器上,Thread.Sleep(1)的結果不一定就是1ms,而可能是以15ms為單位。

經過鍥而不舍的搜查,後來有了驚奇的發現! 原來這個事實是可以改變的,多媒體程式庫中有個timeBeginPeriod的API,可以設定Thread.Sleep的時間解析度,例如以下的寫法:

private static void TestSleep()

{

    timeBeginPeriod(1);

    Thread.Sleep(1);

    timeEndPeriod(1);

}

[DllImport("winmm.dll")]

internal static extern uint timeBeginPeriod(uint period);

[DllImport("winmm.dll")]

internal static extern uint timeEndPeriod(uint period);

於是我在上次的TestThreadSleep程式中加上timeBeginPeriod, timeEndPeriod的邏輯,測試結果馬上大不相同,Thread.Sleep不再懶床,原則上會逼近所要求的等待時間。以DL360為例,

使用前:

Thread.Sleep(25) Test:
23.968 ms, 31.266 ms, 30.447 ms, 31.165 ms, 31.041 ms, Avg = 29.577 ms
Thread.Sleep(15) Test:
15.682 ms, 15.291 ms, 15.557 ms, 15.521 ms, 15.550 ms, Avg = 15.520 ms
Thread.Sleep(10) Test:
15.486 ms, 15.463 ms, 15.682 ms, 15.418 ms, 15.517 ms, Avg = 15.513 ms
Thread.Sleep(5) Test:
15.472 ms, 15.552 ms, 15.535 ms, 15.569 ms, 15.709 ms, Avg = 15.568 ms
Thread.Sleep(1) Test:
15.290 ms, 15.539 ms, 15.559 ms, 15.551 ms, 15.554 ms, Avg = 15.499 ms

使用後:

Thread.Sleep(25) Test:
5.680 ms, 25.015 ms, 25.268 ms, 25.319 ms, 25.117 ms, Avg = 21.280 ms
Thread.Sleep(15) Test:
15.637 ms, 15.420 ms, 15.548 ms, 15.556 ms, 15.560 ms, Avg = 15.544 ms
Thread.Sleep(10) Test:
10.755 ms, 10.576 ms, 10.626 ms, 10.674 ms, 10.536 ms, Avg = 10.633 ms
Thread.Sleep(5) Test:
5.591 ms, 5.684 ms, 5.789 ms, 5.786 ms, 5.798 ms, Avg = 5.730 ms
Thread.Sleep(1) Test:
1.761 ms, 1.783 ms, 1.866 ms, 1.902 ms, 1.735 ms, Avg = 1.810 ms

不過,使用timeBeginPeriod時要注意,原則上調動它會造成系統不必要的負擔,記得不用了馬上timeEndPeriod恢復原狀。


Comments

# by fanncy

謝謝您的分享,非常感謝。

# by 小熊子

https://dotblogs.com.tw/yc421206/2013/04/25/102300 [C#.NET][Thread] 善用 SpinWait 處理 執行緒空轉 以利提昇效能 15:26 當我們在處理一個執行緒時,若需要同步等待時,以往可能會常用 Thread.Sleep,但 Thread.Sleep 會消耗 CPU 的時間配置,所以我們可以使用 Thread.SpinWait 方法 、SpinWait 結構 在 .NET4.0 以前,可以使用 Thread.SpinWait 方法 在 .NET4.0 以後,可以使用 SpinWait 結構 參考: https://docs.microsoft.com/zh-tw/dotnet/api/system.threading.spinwait.spinuntil?view=netcore-3.1 若在非同步的方法中要做 Thread.Sleep是非常危險,建議使用上述方法放開 若是 Thread.Sleep(0) 可參考await Task.Yield() 比較接近用法,供參考

Post a comment