亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

C#中自定義高精度Timer定時器的實例教程

2020-01-24 01:10:55
字體:
來源:轉載
供稿:網友

1、背景

在C#里關于定時器的類就有3個:
(1)定義在System.Windows.Forms里  
(2)定義在System.Threading.Timer類里  
(3)定義在System.Timers.Timer類里

Timer 用于以用戶定義的事件間隔觸發事件。Windows 計時器是為單線程環境設計的,其中,UI 線程用于執行處理。它要求用戶代碼有一個可用的 UI 消息泵,而且總是在同一個線程中操作,或者將調用封送到另一個線程。

使用此計時器時,請使用控件的Tick事件執行輪詢操作,或在指定的時間內顯示啟動畫面。每當 Enabled 屬性設置為true且Interval屬性大于0時,將引發Tick事件,引發的時間間隔基于Interval屬性設置。
System.Windows.Forms.Timer是應用于WinForm中的,他是通過Windows消息機制實現的,類似于VB或Delphi中的Timer控件,內部使用API  SetTimer實現的。他的主要缺點是計時不精確,而且必須有消息循環,Console  Application(控制臺應用程式)無法使用。   
System.Timers.Timer和System.Threading.Timer很類似,他們是通過.NET  Thread  Pool實現的,輕量,對應用程式、消息沒有特別的需要。System.Timers.Timer還能夠應用于WinForm,完全取代上面的Timer控件。

然而其精度都不高(一般情況下 15ms 左右),難以滿足一些場景下的需求。

在進行媒體播放、繪制動畫、性能分析以及和硬件交互時,可能需要 10ms 以下精度的定時器。這里不討論這種需求是否合理,它是確實存在的問題,也有相當多的地方在討論,說明這是一個切實的需求。然而,實現它并不是一件輕松的事情。

這里并不涉及內核驅動層面的定時器,只分析在 .NET 托管環境下應用層面的高精度定時器實現。

Windows 不是實時操作系統,所以任何方案都無法絕對保證定時器的精度,只是能盡量減少誤差。所以,系統的穩定性不能完全依賴于定時器,必須考慮失去同步時的處理。

2、等待策略

想要實現高精度定時器,必然需要等待和計時兩種基礎功能。等待用來跳過一定時間間隔,計時可以進行時間檢查,用以調整等待時間。

等待策略實際就是兩種:

自旋等待:讓 CPU 空轉消耗時間,占用大量 CPU 時間,但是時間高度可控。
阻塞等待:線程進入阻塞狀態,出讓 CPU 時間片,在等待一定時間后再由操作系統調度回到運行狀態。阻塞時不占用 CPU,然而需要操作系統調度,時間難以控制。
可以看到二者各有優劣,應該按照不同需求進行不同的實現。

而計時機制可以說能用的只有一種,就是Stopwatch類。它內部使用了系統 API QueryPerformanceCounter/ QueryPerformanceFrequency來進行高精度計時,依賴于硬件,它的精度可以高達幾十納秒,非常適合用來實現高精度定時器。

所以難點在于等待策略,下面先分析簡單的自旋等待。

2.1自旋等待

可以使用Thread.SpinWait(int iteration)來進行自旋,也就是讓 CPU 在一個循環里空轉,iteration參數是迭代次數。.NET Framework 中不少同步構造都用到了它,用來等待一小段時間,減少上下文切換的開銷。

這里很難根據iteration來計算消耗的時間,因為 CPU 速度可能是動態的。所以需要結合使用Stopwatch。偽代碼如下:

var 等待開始時間 = 當前計時;while ((當前計時 - 等待開始時間) < 需要等待的時間){  自旋;}

寫成實際代碼:

void Spin(Stopwatch w, int duration){  var current = w.ElapsedMilliseconds;  while ((w.ElapsedMilliseconds - current) < duration)    Thread.SpinWait(10);}

這里的w是一個已經啟動的Stopwatch,為了演示簡單使用了ElapsedMilliseconds屬性,精度是毫秒級的,使用ElapsedTicks屬性就可以獲得更高的精度(微秒級)。

然而如前所述,這樣精度高但是是以消耗 CPU 時間為代價的,這樣實現定時器會讓一個 CPU 核心滿負荷工作(如果執行的任務也沒有阻塞的話)。相當于浪費了一個核心,在有些時候不太現實(比如核心很少甚至是單核的虛擬機上),所以需要考慮阻塞等待。

2.2阻塞等待

阻塞等待會把控制權交給操作系統,這樣就必須確保操作系統能夠及時的將定時器線程調度回運行狀態。默認情況下,Windows 的系統定時器精度是 15.625ms,也就是說時間切片是這個尺寸。如果線程阻塞,出讓其時間片進行等待,再被調度運行的時間至少是一個切片 15.625ms。那么必須減少時間切片的長度,才有可能實現更高的精度。

可以通過系統 API timeBeginPeriod來修改系統定時器精度到 1ms(它內部使用了沒有給出文檔的NtSetTimerResolution,這個 API 可以修改到 0.5ms)。不需要的時候使用timeEndPeriod還原。

修改系統定時器精度有副作用。它會增加上下文切換的開銷,增加耗電量,降低系統整體性能。然而,很多程序都不得不這么做,因為沒有其它方式能獲得更高的定時器精度。比如基于 WPF 的程序(包括 Visual Studio)、使用 Chromium 內核的應用(Chrome、QQ)、多媒體播放器、游戲等等很多程序都會在一定時間內把系統定時器精度修改到 1ms。(查看方法見后面)

所以實際上這個副作用在桌面環境已經成為常態。而且從 Windows 8 開始,這個副作用減弱了。

在 1ms 的系統定時器精度前提下,可以使用三種方式實現阻塞等待:

(1)Thread.Sleep
(2)WaitHandle.WaitOne
(3)Socket.Poll
另外,多媒體定時器timeSetEvent也使用了阻塞的方式。

(1)Thread.Sleep

它的參數使用毫秒單位,所以最多只能精確到 1ms。不過事實上很不穩定,Thread.Sleep(1)會在 1ms 與 2ms 兩種狀態間跳動,也就是可能會產生 +1ms 多的誤差。

實測發現,沒有任務負載的情況下(純粹循環調用Sleep(1)),阻塞時長穩定在 2ms;而有任務負載時,則至少會阻塞 1ms。這和其它兩種阻塞方式不同,詳見后文。

如果需要修正這個誤差,可以在阻塞 n 毫秒時,使用Sleep(n-1),并通過Stopwatch計時,剩余等待時間用Sleep(0)、Thread.Yield或自旋來補充。

Sleep(0)會出讓剩余的 CPU 時間片給優先級相同的線程,而Thread.Yield是出讓剩余的 CPU 時間片給運行在同一核心上的線程。在出讓的時間片結束后,其會被重新調度。一般情況下,整個過程可以在 1ms 之內完成。

Thread.Sleep(0)和Thread.Yield在 CPU 高負載情況下非常不穩定,實測可能會阻塞高達 6ms 時間,所以可能會產生更多的誤差。因此誤差修正最好通過自旋方式實現。

(2)WaitHandle.WaitOne

WaitHandle.WaitOne與Thread.Sleep類似,參數也是毫秒單位。

不同之處是,沒有任務負載的情況下(純粹循環調用WaitOne(1)),阻塞時長穩定在 1.5ms;而有任務負載時,則可能僅阻塞近乎于 0 的時間(猜測是它僅阻塞到當前時間片結束,尚未找到具體的文檔說明)。所以它阻塞的時長范圍是 0 到 2ms 多。

WaitHandle.WaitOne(0)是用來測試等待句柄狀態的,它并不阻塞,所以用它來進行誤差修正類似于自旋,但不如直接使用自旋可靠。

(3)Socket.Poll

Socket.Poll方法的參數是以微秒為單位,理論上,它是使用了網卡的硬件來定時,精度很高。然而,由于阻塞的實現仍然要依賴線程,所以它也只能達到 1ms 的精度。

它的優勢是比Thread.Sleep和WaitHandle.WaitOne要更穩定,誤差也更小,可以不需要修正,但要占用一個 Socket 端口。

沒有任務負載的情況下(純粹循環調用Poll(1)),阻塞時長穩定在 1ms;而有任務負載時,則和WaitOne類似,可能僅阻塞近乎于 0 的時間。所以它阻塞的時長范圍是 0 到 1ms 多。

Socket.Poll(0)是用來測試 Socket 狀態的,但它會阻塞,而且可能阻塞高達 6ms,所以不能用它來進行誤差修正。

2.3timeSetEvent

timeSetEvent和之前提到的timeBeginPeriod一樣屬于 winmm.dll 提供的多媒體定時器功能。它可以直接當作定時器使用,也是提供 1ms 的精度。在不需要的時候使用timeKillEvent來關閉。

它的穩定性和精度也很高,如果需要 1ms 的定時,而又不能使用自旋,那么這是最理想的方案。

雖然 MSDN 上說timeSetEvent是個過時的方法,應該用CreateTimerQueueTimer替換。但是CreateTimerQueueTimer精度和穩定性都不如多媒體定時器,所以在需要高精度的時候,只能使用timeSetEvent。

3、定時器實現

需要注意的是,無論自旋還是阻塞,顯然定時器都應該運行在獨立的線程,不能干擾使用方線程工作。而對于高精度定時器來說,觸發事件以執行任務的線程一般都在定時器線程內,而不是再使用獨立的任務線程。

這是因為高精度定時場景下,執行任務的時間開銷很可能大于定時器的時間間隔,如果默認就在其它線程執行任務,可能導致占用大量線程。所以應該把控制權交給用戶,讓用戶在需要的時候自行調度任務執行的線程。

3.1觸發模式

由于在定時器線程執行任務,所以定時器的觸發就產生了三種模式。以下是它們的說明和主循環偽代碼:

(1)固定時間框架
比如間隔 10ms,任務 7-12ms,則會按照等待 10ms 、任務 7ms、等待 3ms、任務 12ms(超時 2ms 失去同步)、任務 7ms、等待 1ms(回到同步)、任務 7ms、等待 3ms、… 進行。就是盡量按照設定好的時間框架來執行任務,只要任務不是始終超時,就可以回到原本的時間框架上。

var 下一幀時間 = 0;while(定時器開啟){  下一幀時間 += 間隔時間;  while (當前計時 < 下一幀時間)  {    等待;  }  觸發任務;}

(2)可推遲時間框架:
上面的例子會按照等待 10ms 、任務 7ms、等待 3ms、任務 12ms(超時,推遲時間框架 2ms)、任務 7ms、等待 3ms、… 進行。超時的任務會推遲時間框架。

var 下一幀時間 = 0;while(定時器開啟){  下一幀時間 += 間隔時間;  if (下一幀時間 < 當前計時)    下一幀時間 = 當前計時  while (當前計時 < 下一幀時間)  {    等待;  }  觸發任務;}

(3)固定等待時間
上面的例子會按照等待 10ms、任務 7ms、等待 10ms、任務 12ms、等待 10ms、任務 7ms… 進行。等待時間始終不變。

while(定時器開啟){  var 等待開始時間 = 當前計時;  while ((當前計時 - 等待開始時間) < 間隔時間)  {    等待;  }  觸發任務;}// 或者:var 下一幀時間 = 0;while(定時器開啟){  下一幀時間 += 間隔時間;  while (當前計時 < 下一幀時間)  {    等待;  }  觸發任務;  下一幀時間 = 當前計時;}

如果使用多媒體定時器(timeSetEvent),它固定實現了第一種模式,而其它的等待策略能夠實現全部三種模式,可以根據需求選擇。

在while循環中的等待可以使用自旋或阻塞,也可以結合它們來達到精度、穩定性和 CPU 開銷的平衡。

另外,由上面的偽代碼可以看出,這三種模式的實現可以統一,能夠做到根據情況切換。

3.2線程優先級

最好把線程優先級調高,以保證定時器能夠穩定工作,減少被搶占的機會。然而需要注意,這在 CPU 資源不足時可能導致低優先級線程的饑餓。也就是說不能讓高優先級線程去等待低優先級線程改變狀態,很有可能低優先級線程沒有機會運行,導致死鎖或類似死鎖的狀態。(見一種類似的饑餓的例子)

線程的最終優先級和進程的優先級有關,所以有時候也需要提高進程優先級(見 C# 中的多線程系列的線程優先級說明)。

4、其它

還有兩點需要注意:

(1)線程安全:定時器在獨立線程運行,其暴露的成員都應該實現線程安全,否則在定時器運行時調用可能會產生問題。
(2)及時釋放資源:多媒體定時器、等待句柄、線程等等這些都是系統資源,在不需要它們的時候應該及時釋放/銷毀。
如何查看系統定時器精度?

簡單的查看可以使用Sysinternals工具包中的 ClockRes,它會顯示如下信息:

Maximum timer interval: 15.625 msMinimum timer interval: 0.500 msCurrent timer interval: 15.625 ms// 或Maximum timer interval: 15.625 msMinimum timer interval: 0.500 msCurrent timer interval: 1.000 ms

如果是想查看哪些程序請求了更高的系統定時器精度,那么運行:

powercfg energy -duration 5

它會監視系統能耗 5s,然后在當前目錄生成一個energy-report.html的分析報告,可以打開它查看。

找到里面的警告部分,會有平臺計時器分辨率:未完成的計時器請求(Platform Timer Resolution:Outstanding Timer Request)信息。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久精品成人动漫| y97精品国产97久久久久久| 国产欧美日韩视频| 国产精品无av码在线观看| 欧美国产一区二区三区| 欧美高清一级大片| 成人xvideos免费视频| 欧美性资源免费| 色偷偷亚洲男人天堂| 国产精品日韩欧美综合| 色婷婷av一区二区三区在线观看| 久久国产精品电影| 97在线视频一区| 国产精品mp4| 亚洲视频在线免费观看| 最近2019中文字幕第三页视频| 自拍偷拍亚洲欧美| 国产精品久久77777| 日韩免费在线免费观看| 亚洲精品自拍偷拍| 最近中文字幕日韩精品| 96pao国产成视频永久免费| 伊人伊成久久人综合网站| 色综合男人天堂| 中文字幕一精品亚洲无线一区| 91在线免费视频| 亚洲精品电影在线观看| 精品欧美激情精品一区| 国产91精品高潮白浆喷水| 疯狂做受xxxx高潮欧美日本| 国产精品久久久久久久久久| 国产成人亚洲综合91| 亚洲一区二区三区视频| 国内精品中文字幕| 色综合久久88色综合天天看泰| 亚洲片国产一区一级在线观看| 亚洲精品免费网站| 国产日韩av在线播放| 欧美成人一区二区三区电影| 狠狠躁夜夜躁人人爽超碰91| 日韩在线视频网| 日韩高清av在线| 深夜福利日韩在线看| 久久人人爽人人| 国产精品高潮呻吟久久av黑人| 欧美极品欧美精品欧美视频| 成人av在线网址| 欧美整片在线观看| 日韩精品视频免费在线观看| 亚洲日本欧美中文幕| 中文字幕亚洲色图| 亚洲精品日韩av| 黑人巨大精品欧美一区二区免费| 伊人久久大香线蕉av一区二区| 欧美精品在线看| 亚洲最大福利视频| 欧美亚洲另类激情另类| 国产剧情日韩欧美| 精品电影在线观看| 国产香蕉精品视频一区二区三区| 欧美另类老肥妇| 宅男66日本亚洲欧美视频| 国产一区在线播放| 国产精品免费一区豆花| 人人爽久久涩噜噜噜网站| 欧美性猛交xxxxx免费看| 清纯唯美日韩制服另类| 日本成熟性欧美| 亚洲精品中文字幕女同| 国产欧美久久久久久| 成人av.网址在线网站| 在线日韩第一页| 久久精品福利视频| 欧美激情xxxxx| 中文字幕亚洲无线码在线一区| 亚洲精品久久久久久久久久久久| 久久久精品国产亚洲| 欧美伊久线香蕉线新在线| 国产视频丨精品|在线观看| 自拍亚洲一区欧美另类| 91精品国产自产在线老师啪| 国产精品久久久久久久久久三级| 在线亚洲国产精品网| 一本色道久久综合亚洲精品小说| 日韩av最新在线观看| 中文日韩在线视频| 亚洲免费小视频| 性色av一区二区三区在线观看| 91高清在线免费观看| 欧美另类老女人| 久久精品电影网站| 国产精品爱啪在线线免费观看| 日韩视频在线免费| 久久国产精品99国产精| 欧美激情视频一区| 国产成人精品综合久久久| 中文字幕在线精品| 在线播放亚洲激情| 欧洲永久精品大片ww免费漫画| 久久伊人精品视频| 成年人精品视频| 日韩欧美高清在线视频| 神马久久桃色视频| 国产日韩在线看| 久精品免费视频| 精品久久久久久国产| 亚洲黄色免费三级| 欧美精品中文字幕一区| 这里只有精品丝袜| 久久伊人91精品综合网站| 91伊人影院在线播放| 亚洲韩国青草视频| 国产精品www| 亚洲人成绝费网站色www| 亚洲精品国产精品国自产观看浪潮| 日本一区二区三区在线播放| 成人免费视频网| 狠狠久久五月精品中文字幕| 成人国产亚洲精品a区天堂华泰| 岛国精品视频在线播放| 最近2019免费中文字幕视频三| 亚洲国内高清视频| 国产97在线观看| 国内精品小视频在线观看| 国产日韩在线视频| 久久久黄色av| 日韩av观看网址| 亚洲成人激情小说| 欧美俄罗斯乱妇| 91精品视频大全| 欧美性极品xxxx做受| 欧美野外wwwxxx| 国产视频亚洲精品| 成人免费视频xnxx.com| 精品二区三区线观看| 国产一区二区免费| 九色成人免费视频| 精品国产91久久久久久老师| 欧美性少妇18aaaa视频| 久久6精品影院| 亚洲区免费影片| 久久久国产在线视频| 亚洲国产日韩一区| 欧美日韩亚洲成人| 97国产精品免费视频| 久久乐国产精品| 亚洲黄色av女优在线观看| 成人久久久久久| 欧美成人剧情片在线观看| 精品久久久久久久久中文字幕| 91国偷自产一区二区三区的观看方式| 欧美放荡办公室videos4k| 亚洲国产精品视频在线观看| 久久亚洲精品成人| 国产伊人精品在线| 欧美激情免费在线| 亚洲天堂成人在线视频| 国产精品爽爽爽爽爽爽在线观看| 国产精品劲爆视频| 成人羞羞国产免费| 国产香蕉精品视频一区二区三区| 亚洲在线视频观看| 中文字幕一区电影| 久久精品一区中文字幕|