程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#中自界說高精度Timer准時器的實例教程

C#中自界說高精度Timer准時器的實例教程

編輯:C#入門知識

C#中自界說高精度Timer准時器的實例教程。本站提示廣大學習愛好者:(C#中自界說高精度Timer准時器的實例教程)文章只能為提供參考,不一定能成為您想要的結果。以下是C#中自界說高精度Timer准時器的實例教程正文


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 ms
Minimum timer interval: 0.500 ms
Current timer interval: 15.625 ms

// 或

Maximum timer interval: 15.625 ms
Minimum timer interval: 0.500 ms
Current timer interval: 1.000 ms

假如是想檢查哪些法式要求了更高的體系准時器精度,那末運轉:

powercfg energy -duration 5

它會監督體系能耗 5s,然後在以後目次生成一個energy-report.html的剖析申報,可以翻開它檢查。

找到外面的正告部門,會有平台計時器分辯率:未完成的計時器要求(Platform Timer Resolution:Outstanding Timer Request)信息。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved