程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> 一個帶自適應功能的高精度計時

一個帶自適應功能的高精度計時

編輯:關於C語言

這應該算我近期的工作了。我最近在做一個HashCache,裡面有個回寫功能,即將有更新的節點內容寫入磁盤。這帶來一個問題,這個HashCache是多線程訪問環境,必須考慮加鎖,由於HashCache內部有淘汰機制,需要利用到一個最近訪問的時間戳來判定,也就是說,無論讀還是寫操作,最終都會對這個時間戳實現寫動作,因此,不存在單寫多讀鎖的調用可能性,只能用普通鎖。 也就是說,這個HashCache,必須通過鎖域封裝,每個客戶來訪問的動作都是排他的,HashCache每次只能由一個客戶訪問。但這會帶來效率問題,由於多任務開發的“大鎖”問題,必然效率低下。但這又是維護正常邏輯必須的,沒有辦法折中。唯一的解決之道,就是盡量縮減每個訪問動作的時間,使每個鎖域粒度最小化,大家快進快出,保證效率。 這其實也是我使用Hash來做這個Cache的原因,Hash的訪問復雜度,基本上是O1)的,這保證我每個鎖域都是O1)的寬度,這使我的HashCache獲得了最大交換能力,也就是訪問能力最大化。 但是,回到開始的問題,我必須考慮回寫邏輯,以一個線程不斷遍歷Hash表的每個節點,把內容有更新的節點,寫回磁盤,這個動作,由於涉及磁盤交互,必然是一個高loading的動作,肯定不是O1)啦,這沒有辦法,那我就得想個什麼辦法來控制這個回寫邏輯的時間片不至於太寬,免得把鎖占據太久,導致上層正常的訪問邏輯被長時間掛住,甚至超時報錯。 這就要求我必須在毫秒級控制時間片的精度。而且,誤差不能太大,我預設大約每次回寫線程工作5ms就退出,等待15ms再繼續,這樣我可以以20ms為周期,每秒鐘完成50個周期左右,外部業務線程獲得750ms左右的時間,而回寫線程獲得250ms的時間,各自完成自己的業務。我試圖通過這種對時間片的精確控制,來保證業務訪問的精度要求。 不過,這就有點麻煩了,我的工程庫,在《0bug-C/C++商用工程之道》中,大家可以看到一個CDeltaTime的類,這是我常用的一個計時器5.5.3小節,P194頁),不過呢,這個計時器有個缺點,精度只有1秒,這顯然不夠,我沒辦法,昨天就重寫了一個高精度計時器,不過一寫起來才發現,這個問題沒想的那麼簡單,呵呵,本來想喝口綠茶,結果被灌了一口二鍋頭。 我花了整整一個下午來做這件事情,中間呢,出於工程化開發的原則,我考慮了很多細節,我想了一下,還是寫篇博文把這個過程展示一下,也許能給大家一點啟發也說不定哈。 這個計時器,我本意還是仿照CDeltaTime類來做一個統計性的類,內部計算DeltaTime,供外部查詢。在《0bug-C/C++商用工程之道》的P193頁,我呢,有個CCountSub的類,它能處理unsigned long的數值,看起來還可以,但我仔細一想,發現一個問題。32bits的OS下,unsigned long的范圍是4Bytes,這個用來存秒呢,可以存幾十年的,不過,要是存毫秒,完蛋了,我算了一下,差不多49天就溢出,這肯定不行。沒辦法,我首先重新做了個統計模塊,專門處理溢出問題。 Code:

  1. class CDWORDCount   
  2. {   
  3. public:   
  4.     CDWORDCount(){Reset();}   
  5.     ~CDWORDCount(){}   
  6.     DWORD Reset(DWORD dwValue=0){return m_dwBegin=m_dwEnd=dwValue;}   
  7.     DWORD SetBegin(DWORD dwValue){return (m_dwBegin=dwValue);}   
  8.     DWORD SetEnd(DWORD dwValue){return m_dwEnd=dwValue;}   
  9.     DWORD GetBegin(void){return m_dwBegin;}   
  10.     DWORD GetEnd(void){return m_dwEnd;}   
  11.     DWORD GetX(void)   
  12.     {   
  13.         if(m_dwEnd<m_dwBegin)   
  14.         {   
  15.             //處理溢出問題   
  16.             int nBeginX=abs(0-m_dwBegin);       //求得m_dwBegin與0之間的差值nBeginX   
  17.             return m_dwEnd+(DWORD)nBeginX;      //m_dwEnd此時的值是相對0之間的溢出部分   
  18.                                                 //真實的差值是nBeginX與m_dwEnd之和   
  19.         }   
  20.         return (m_dwEnd-m_dwBegin);   
  21.     }   
  22. private:   
  23.     DWORD m_dwBegin;   
  24.     DWORD m_dwEnd;   
  25. };  
大家注意GetX這個函數,是求Begin和End的差值,由於windows下,求毫秒的函數GetTickCount返回的是DWORD,因此,我這個工具類也主要處理DWORD這個類型。具體函數意思我不用寫注釋了吧,看英文都看得懂。 當然,按我團隊的規矩,還是要給一個測試函數,大家可以看看其對溢出邏輯的處理是正確的: Code:
  1. inline void TestCDWORDCount(void)   
  2. {   
  3.     CDWORDCount Count;   
  4.     Count.SetBegin(0xFFFFFFFF);   
  5.     Count.SetEnd(5);                            //此時的End相當於0x100000005   
  6.     printf("%ld\n",Count.GetX());   
  7.     printf("%ld\n",abs(0-0xFFFFFFFF));   
  8. }  
ok,有了這個工具,我准備開始寫計時器,我先完成了基本部分,嗯,大家注意,為了保證精度,這裡面全部使用的是C++的內聯函數寫法,即函數會在編譯時在調用處展開,節約壓棧、彈棧的動作開銷,我們這是在計時,省一點總是好的。 先給大家一個宏吧,這是求毫秒級精度的系統函數的跨平台封裝。《0bug-C/C++商用工程之道》裡面有,各位讀者可以看看。 Code:
  1. #define _GetTimeOfDay GetTickCount   
由於寫這個計時器一波三折,我也是分幾次才寫好,因此,這裡先給基本類,後面再逐漸介紹幾套計時方案。 Code:
  1. class CTonyDeltaMicroSeconds   
  2. {   
  3. public:   
  4.     CTonyDeltaMicroSeconds(){Reset();}   
  5.     ~CTonyDeltaMicroSeconds(){}   
  6.     void Reset(void)   
  7.     {   
  8.         m_Count.Reset(_GetTimeOfDay());   
  9.         m_nFix=0;  //這個回頭再說   
  10.         m_Mean.Reset();  //這個回頭再說   
  11.     }   
  12.     void PrintInfo(void)   
  13.     {   
  14.         printf("m_nFix=%d\n",m_nFix);   
  15.         m_Mean.PrintInfo();   
  16.     }   
  17.     DWORD TouchBegin(void){return m_Count.SetBegin(_GetTimeOfDay());}   
  18.     DWORD TouchEnd(void){return m_Count.SetEnd(_GetTimeOfDay());}   
  19.     DWORD GetBegin(void){return m_Count.GetBegin();}   
  20.     DWORD GetEnd(void){return m_Count.GetEnd();}   
  21.     DWORD GetDeltaMicroSeconds(void){return m_Count.GetX();}   
  22. private:   
  23.     CDWORDCount m_Count;   
  24.     CTonyMean m_Mean;  //這個回頭再說   
  25.     int m_nFix;  //這個回頭再說   
  26. };  
大家注意,其實基本類很簡單,都在調用前一個類,僅僅是把基數綁定在系統取得的毫秒數上。 然後,我開始考慮怎麼樣計時,其實基本原理很簡單,我內部保存了Begin和End,兩次時間點的毫秒差,就是內部的DeltaTime,這個和外部傳進來的時間段MaxDelta比較一下,如果內部DeltaTime>外部時差,則表示時間到了,可以返回真,否則返回假,所以,我寫了第一個計時函數TimeIsUp0。 Code:
  1. bool TimeIsUp0(const DWORD dwMaxDeltaMicroSeconds)   
  2. {   
  3.     //直接比較內部的時間差和外部的判斷時長,   
  4.     //如果外部的判斷時長<=內部時間差,則返回真   
  5.     //無修訂,精度誤差15ms左右   
  6.     return (dwMaxDeltaMicroSeconds<=GetDeltaMicroSeconds());   
  7. }  
簡單吧,呵呵。 可是效果不好,我測了一下,其精度誤差在15~16ms左右,即我要求睡眠1ms,它返回真的時候是15ms。暈哦,這咋控制精度。 沒辦法,我開始試圖修訂這個算法。 這中間肯定要修訂誤差,不過,這個誤差方法是我以前寫游戲的經驗,其實就是路徑跟蹤算法啦。當玩家控制的主角在屏幕上跑來跑去的時候,計算機控制的敵人要追蹤主角的位置,總是不斷計算自己和主角之間的坐標差自己坐標 - 主角坐標),如果這個坐標差為-,表示目前自己的坐標小了,需要增加,反之則需要減少。X、Y都可以這麼計算。這個體現增加和減少的變量,叫做修訂因子,它和自己的絕對坐標之和,就是新的坐標。 當然,這個修訂因子還可以調整加速度,即如果每次都少,則修訂因子不斷+1,每次和自己坐標之和,步距就大,而每次都多,步距其實也大,但是,隨著兩人逐漸靠近,如果兩人的,比如X坐標,很接近,要麼大一點,要麼小一點,則根據算法,這個修訂因子就在-1,0,+1之間跳變,其實相當於X不怎麼變動。這是動態的計算變化。 這段可能有點繞,大家仔細看吧,我已經很努力了,但是也沒辦法講更清楚,個別朋友可以給我發信息詢問。 我是這麼考慮的: 1、每次判定時間到了,會得到一個真,此時,內部的DeltaTime和外部要求的時間段MaxDelta,肯定不太可能一樣,有誤差是正常的。 2、但是,可以考慮修訂,即我內部保留一個修訂因子int m_nFix;,這個值本來是0,但是,如果我求得一個真,此時可以計算一個差值DeltaTime-MaxDelta,這表示誤差,這個誤差如果<0,則表示小了,修訂因子需要+1,反之-1。 3、每次進來判定,判定的是MaxDelta+m_nFix的值,即利用m_nFix修訂因子來影響判定結果。最終得出較為精確的時間。 由於上述思想,我寫了第二個判定函數: Code:
  1. bool TimeIsUp1(const DWORD dwMaxDeltaMicroSeconds)   
  2. {   
  3.     //初級修訂,每次判斷時,外部條件需要加上一個修訂值再判斷   
  4.     //以這個修訂值來抵消誤差   
  5.     //dwMaxDeltaMicroSeconds=100,nDeltaT=110,表示外部程序循環精度不高,110ms才訪問一次本接口   
  6.     //則nDeltaT-dwMaxDeltaMicroSeconds=10>0,修訂值-1初值為0)   
  7.     //下次判斷時,dwMaxDeltaMicroSeconds=100+(-1)=99,   
  8.     //這使得nCompNumber更容易<=nDeltaT,也就是說,更快地返回時間到的真標志   
  9.     //dwMaxDeltaMicroSeconds=100,nDeltaT=90,表示外部程序循環精度太高,90ms就訪問一次本接口   
  10.     //則nDeltaT-dwMaxDeltaMicroSeconds=-10<0,修訂值+1初值為0)   
  11.     //下次判斷時,dwMaxDeltaMicroSeconds=100+(1)=101,   
  12.     //這使得nCompNumber更不容易<=nDeltaT,也就是說,更慢地返回時間到的真標志   
  13.     //修訂值總是在跳變,當出現一次為真的成功比較,即時間到,   
  14.     //此時nDeltaT大約相當於外部要求的dwMaxDeltaMicroSeconds,   
  15.     //二者之間的差值nDeltaT-nMaxDeltaMicroSeconds即作為修訂值的調整依據   
  16.     //調整後的修訂值,影響下面幾輪的比較動作   
  17.     //由於這是使用絕對差值nDeltaT-nMaxDeltaMicroSeconds在調整修訂值m_nFix,   
  18.     //導致m_nFix的抖動很大,誤差還是比較大,經實測,誤差在+-6~7ms左右,   
  19.     //即100ms的求精,得到94~106左右的范圍,誤差還是很大   
  20.     int nDeltaT=(int)GetDeltaMicroSeconds();                    //獲得Begin和End之間的時間差   
  21.     int nMaxDeltaMicroSeconds=(int)dwMaxDeltaMicroSeconds;      //業務判斷的毫秒數取整型   
  22.     int nCompNumber=nMaxDeltaMicroSeconds+m_nFix;               //業務判斷毫秒數和修正值累加,獲得比較值   
  23.     bool bRet=(nCompNumber<=nDeltaT);                           //比較值和內部保存的時間差比較   
  24.     if(bRet)   
  25.     {   
  26.         //如果為真,表示時間到,調整修正值   
  27.         int nFixX=nDeltaT-nMaxDeltaMicroSeconds;                //取得修訂系數,實際存儲的時間差減去外部給定的差值   
  28.         if(0<nFixX) m_nFix--;                                   //如果修訂系數為負數,表示nMaxDeltaMicroSeconds太大,   
  29.                                                                 //修正值-1,下次的nCompNumber就會小1   
  30.         else if(0>nFixX) m_nFix++;                              //否則,修正值+1,下次的nCompNumber就會大1   
  31.                                                                 //這種修訂方法取絕對值,m_nFix抖動很大,精度不高   
  32.     }   
  33.     return bRet;   
  34. }  
不過出來一測,發現精度還是太低,有改善,我獲得了7~8ms的誤差精度,即我要1ms,得到7~8ms,要100ms,得到93~107ms的誤差范圍。 我仔細分析了一下,推測可能是直接使用DeltaTime-MaxDelta的絕對值來做判定,動作太大了,m_nFix跳變抖動得厲害,影響了精度。 那,有個簡單的辦法來實現這個邏輯,就是使用多次DeltaTime-MaxDelta的平均值來影響m_nFix,減小抖動。不過,這帶來一個問題,我手邊還沒有一個合用的平均值計算模塊,想了一下,我干脆寫了一個。 Code:
  1. #include <float.h>   
  2. //判定一個double數是否為0.0,相當於if(0.0==dValue)   
  3. inline bool DoubleIsZero(double dValue){return (abs(dValue)<DBL_EPSILON);}   
  4. //判定兩個double數是否絕對相等,相當於if(dA==dB)   
  5. inline bool DoubleIsEqual(double dA,double dB){return DoubleIsZero(dA-dB);}   
  6. class CTonyMean   
  7. {   
  8. public:   
  9.     CTonyMean(){Reset();}   
  10.     ~CTonyMean(){}   
  11.     void Reset(void)   
  12.     {   
  13.         m_dAllCount=0.0;   
  14.         m_nTimes=0;   
  15.         m_dMean=0.0;   
  16.         m_nOnlyPositiveNumberFlag=0;   
  17.         m_nOnlyNegativeNumberFlag=0;   
  18.     }   
  19.     double Push(double dValue)   
  20.     {   
  21.         //printf("Push(%f)\n",dValue);  //調試代碼,可以刪除   
  22.         //統計是否只處理單一數據種類正數或者負數)   
  23.         if(0>dValue) m_nOnlyPositiveNumberFlag=1;   
  24.         if(0<dValue) m_nOnlyNegativeNumberFlag=1;   
  25.         m_nTimes++;   
  26.         //由於本類沒有限定輸入的dValue值是否為正數或者   
  27.         //因此m_dAllCount可能會由於單一數種溢出導致為0.0,   
  28.         //也可能因為正負數抵消為0.0   
  29.         //必須分別處理   
  30.         m_dAllCount+=dValue;   
  31.         //如果據統計,有史以來只使用純正數,或者純負數,   
  32.         //則處理總累加器溢出   
  33.         //否則則認為是正負數抵消的0.0,無需處理   
  34.         if(OnlyOneTypeNumber())   
  35.             if(DoubleIsZero(m_dAllCount))   
  36.                 ResetByOverflow();   
  37.         //由於m_nTimes總是先+1後使用,   
  38.         //因此,此處的m_nTimes==0一定是溢出   
  39.         //使用溢出處理方案   
  40.         if(!m_nTimes) ResetByOverflow();   
  41.         //計算平均值   
  42.         m_dMean=m_dAllCount/(double)(m_nTimes);   
  43.         return GetMean();   
  44.     }   
  45.     //友好接口,大多數時候,大家在用整數做值,因此,允許直接輸入整數值   
  46.     double Push(int nValue){return Push((double)nValue);}   
  47.     //求得平均數   
  48.     double GetMean(void){return m_dMean;}   
  49.     //內部打印   
  50.     void PrintInfo(void)   
  51.     {   
  52.         printf("CTonyMean: AllCount=%f, Times=%d, Mean=%f\n",   
  53.             m_dAllCount,m_nTimes,m_dMean);   
  54.         printf("Positive=%d, Negative=%d, OnlyOneTypeNumber()=%d\n",   
  55.             m_nOnlyPositiveNumberFlag,   
  56.             m_nOnlyNegativeNumberFlag,   
  57.             OnlyOneTypeNumber());   
  58.     }   
  59. private:   
  60.     //是否是相同類型的數字判斷   
  61.     int OnlyOneTypeNumber(void)   
  62.     {return (m_nOnlyPositiveNumberFlag^m_nOnlyNegativeNumberFlag);}   
  63.     //溢出處理方案,設置次數為1,   
  64.     //設置總累計數為以前求的平均值   
  65.     //二者相除,平均值不變   
  66.     //以便保留以往的平均值繼續計算結果。   
  67.     void ResetByOverflow(void)   
  68.     {   
  69.         //把總累加器恢復為上一輪的平均值   
  70.         m_dAllCount=m_dMean;   
  71.         //m_nTimes無條件設置為1   
  72.         m_nTimes=1;   
  73.     }   
  74.     unsigned int m_nTimes;                  //總次數   
  75.     double m_dAllCount;                     //總累加器   
  76.     double m_dMean;                         //平均值   
  77.     unsigned int m_nOnlyPositiveNumberFlag; //有史以來,存儲過正數標志   
  78.     unsigned int m_nOnlyNegativeNumberFlag; //有史以來,存儲過負數標志   
  79. };   
  80. inline void TestCTonyMean(void)   
  81. {   
  82.     CTonyMean Mean;   
  83.        
  84.     Mean.Reset();   
  85.     Mean.Push(1);   
  86.     Mean.Push(2);   
  87.     Mean.PrintInfo();   
  88.        
  89.     Mean.Reset();   
  90.     Mean.Push(-1);   
  91.     Mean.Push(-2);   
  92.     Mean.PrintInfo();   
  93.   
  94.   
  95.     Mean.Reset();   
  96.     Mean.Push(-1);   
  97.     Mean.Push(2);   
  98.     Mean.PrintInfo();   
  99. }  
我想我注釋挺明白的,就不多說了吧。大家現在知道計時器裡面的CTonyMean m_Mean;怎麼來的了吧。呵呵。 嗯,這段代碼調整過,原來的老代碼,只能處理純正數或者有正負數,現在調整為可以同時處理純正數,正負數,以及純負數三種情況,這樣適用面更廣一點。嗯,我順手加了個測試代碼,大家有興趣可以run起來看看。 然後,我寫了第三段精確計時邏輯。   Code:
  1. bool TimeIsUp2(const DWORD dwMaxDeltaMicroSeconds)   
  2. {   
  3.     //高級修訂,利用平均值來優化m_nFix的算法,降低抖動   
  4.     //計算原理同TimeIsUp1   
  5.     //每次不再以nDeltaT-nMaxDeltaMicroSeconds作為m_nFix的修訂依據   
  6.     //而是以多次nDeltaT-nMaxDeltaMicroSeconds的平均值,作為m_nFix的修訂依據   
  7.     //經實測,m_nFix的抖動較小,獲得+-0.003ms的左右時間精度   
  8.     //即100ms,獲得99.997ms~100.003ms的時間精度,1ms求精,可以得到0.997~1.003ms的計時精度   
  9.     int nDeltaT=(int)GetDeltaMicroSeconds();                    //獲得Begin和End之間的時間差   
  10.     int nMaxDeltaMicroSeconds=(int)dwMaxDeltaMicroSeconds;      //業務判斷的毫秒數取整型   
  11.     int nCompNumber=nMaxDeltaMicroSeconds+m_nFix;               //業務判斷毫秒數和修正值累加,獲得比較值   
  12.     bool bRet=(nCompNumber<=nDeltaT);                           //比較值和內部保存的時間差比較   
  13.     if(bRet)   
  14.     {   
  15.         m_Mean.Push(nDeltaT-nMaxDeltaMicroSeconds);             //此處計算平均值   
  16.         if(0.0<m_Mean.GetMean()) m_nFix--;                      //以平均值的正負性來調整修訂值   
  17.         else if(0.0>m_Mean.GetMean()) m_nFix++;   
  18.   
  19.     }   
  20.     return bRet;   
  21. }  
大家請注意平均值計算的代入。 這段代碼效果很好,我測試了一下,獲得了+-0.003ms左右的精度。 當然,我想了一下,干脆,這些個代碼全部提供,所以我寫了個集中的函數,利用switch選擇用哪種計時精度。 Code:
  1. bool TimeIsUp(const DWORD dwMaxDeltaMicroSeconds,int nModel=2)   
  2. {   
  3.     bool bRet=false;   
  4.     TouchEnd();   
  5.     switch(nModel)   
  6.     {   
  7.     default:        //其他數取默認值   
  8.     case 0:         //普通模式   
  9.         bRet=TimeIsUp0(dwMaxDeltaMicroSeconds);   
  10.         break;   
  11.     case 1:         //加入了誤差修訂   
  12.         bRet=TimeIsUp1(dwMaxDeltaMicroSeconds);   
  13.         break;   
  14.     case 2:         //加入了平均值誤差修訂   
  15.         bRet=TimeIsUp2(dwMaxDeltaMicroSeconds);   
  16.         break;   
  17.     }   
  18.     return bRet;   
  19. }  
嗯,這是判定邏輯,有時候,業務層可能不想自己做個循環頻繁判定,因此需要這個邏輯自己提供一個等待睡眠的函數,我也寫了一個。 Code:
  1. void Wait4(const DWORD dwMaxDeltaMicroSeconds,int nModel=2)   
  2. {while(!TimeIsUp(dwMaxDeltaMicroSeconds,nModel)){TonyXiaoMinSleep();}}  
嗯,沒有看過《0bug-C/C++商用工程之道》這本書的朋友,可能看不懂TonyXiaoMinSleep();這個函數,那簡單,直接用Sleep0)代替好了。 最後,我寫了一段代碼測試一下,由於這裡面有動態跟蹤邏輯,因此,前面幾次測試肯定不准,需要工作一段時間,m_nFix調整得差不多了,精度就高了,因此,我寫了如下邏輯。每種模式,0、1、2三種,分別測試5輪,每輪測試1000次,取平均值,來看測試的精度。 精度測試很簡單,我用個計數器,每次疊加DeltaTime這個模塊內部的Delta值,最後除以總的訪問次數,就得到平均每次的毫秒數。 Code:
  1. #define TestCDeltaMicroSeconds_MAX_COUNT 1000   
  2. inline void TestCDeltaMicroSeconds(void)   
  3. {   
  4.     CTonyMean TimeMeadPreModel;   
  5.     CTonyDeltaMicroSeconds dms;   
  6.     int i=0;   
  7.     int j=0;   
  8.     int k=0;   
  9.     int nCount=0;   
  10.     double dMS=0.0;   
  11.     for(i=0;i<3;i++)   
  12.     {   
  13.         TimeMeadPreModel.Reset();   
  14.         for(j=0;j<5;j++)   
  15.         {   
  16.             printf("Test: model=%d, try=%d, test %d times pre try\n",i,j,TestCDeltaMicroSeconds_MAX_COUNT);   
  17.             nCount=0;   
  18.             dms.Reset();   
  19.             for(k=0;k<TestCDeltaMicroSeconds_MAX_COUNT;k++)   
  20.             {   
  21.                 dms.TouchBegin();   
  22.                 //while(!dms.TimeIsUp(1,i)){}//{Sleep(10);}   
  23.                 dms.Wait4(1,i);   
  24.                 nCount+=dms.GetDeltaMicroSeconds();   
  25.             }   
  26.             dMS=(double)nCount/(double)TestCDeltaMicroSeconds_MAX_COUNT;   
  27.             TimeMeadPreModel.Push(dMS);   
  28.             printf("Model %d, try=%d %d times: Mean= %f ms\n",i,j,TestCDeltaMicroSeconds_MAX_COUNT,dMS);   
  29.             printf("-----------\n");   
  30.         }   
  31.         printf("Model %d Mean: %f ms\n",i,TimeMeadPreModel.GetMean());   
  32.         printf("=====================================\n");   
  33.     }   
  34. }  
運行結果如下: Code:
  1. Test: model=0, try=0, test 1000 times pre try  
  2. Model 0, try=0 1000 times: Mean= 15.625000 ms   
  3. -----------   
  4. Test: model=0, try=1, test 1000 times pre try  
  5. Model 0, try=1 1000 times: Mean= 15.625000 ms   
  6. -----------   
  7. Test: model=0, try=2, test 1000 times pre try  
  8. Model 0, try=2 1000 times: Mean= 15.625000 ms   
  9. -----------   
  10. Test: model=0, try=3, test 1000 times pre try  
  11. Model 0, try=3 1000 times: Mean= 15.625000 ms   
  12. -----------   
  13. Test: model=0, try=4, test 1000 times pre try  
  14. Model 0, try=4 1000 times: Mean= 15.625000 ms   
  15. -----------   
  16. Model 0 Mean: 15.625000 ms    //第一種模式,差不多15.625ms的精度   
  17. =====================================   
  18. Test: model=1, try=0, test 1000 times pre try  
  19. Model 1, try=0 1000 times: Mean= 7.813000 ms   
  20. -----------   
  21. Test: model=1, try=1, test 1000 times pre try  
  22. Model 1, try=1 1000 times: Mean= 7.812000 ms   
  23. -----------   
  24. Test: model=1, try=2, test 1000 times pre try  
  25. Model 1, try=2 1000 times: Mean= 7.813000 ms   
  26. -----------   
  27. Test: model=1, try=3, test 1000 times pre try  
  28. Model 1, try=3 1000 times: Mean= 7.812000 ms   
  29. -----------   
  30. Test: model=1, try=4, test 1000 times pre try  
  31. Model 1, try=4 1000 times: Mean= 7.813000 ms   
  32. -----------   
  33. Model 1 Mean: 7.812600 ms    //第二種模式,差不多7.8ms的精度   
  34. =====================================   
  35. Test: model=2, try=0, test 1000 times pre try  
  36. Model 2, try=0 1000 times: Mean= 1.000000 ms   
  37. -----------   
  38. Test: model=2, try=1, test 1000 times pre try  
  39. Model 2, try=1 1000 times: Mean= 1.000000 ms   
  40. -----------   
  41. Test: model=2, try=2, test 1000 times pre try  
  42. Model 2, try=2 1000 times: Mean= 1.000000 ms   
  43. -----------   
  44. Test: model=2, try=3, test 1000 times pre try  
  45. Model 2, try=3 1000 times: Mean= 1.000000 ms   
  46. -----------   
  47. Test: model=2, try=4, test 1000 times pre try  
  48. Model 2, try=4 1000 times: Mean= 1.000000 ms   
  49. -----------   
  50. Model 2 Mean: 1.000000 ms    //第三種模式,精確計量出1ms   
  51. =====================================  
呵呵,當然,由於這個模塊本意是要跨線程工作,我這裡給出它的資源鎖封裝版本。 Code:
  1. class CTonyDeltaMicroSecondsWithLock   
  2. {   
  3. public:   
  4.     CTonyDeltaMicroSecondsWithLock(){}   
  5.     ~CTonyDeltaMicroSecondsWithLock(){}   
  6. public:   
  7.     void Reset(void){TONY_LOCK_CALL(m_TonyDeltaMicroSeconds.Reset())}   
  8.     void PrintInfo(void){TONY_LOCK_CALL(m_TonyDeltaMicroSeconds.PrintInfo());}   
  9.     DWORD TouchBegin(void){TONY_DWORD_LOCK_CALL(m_TonyDeltaMicroSeconds.TouchBegin());}   
  10.     DWORD TouchEnd(void){TONY_DWORD_LOCK_CALL(m_TonyDeltaMicroSeconds.TouchEnd());}   
  11.     DWORD GetBegin(void){TONY_DWORD_LOCK_CALL(m_TonyDeltaMicroSeconds.GetBegin());}   
  12.     DWORD GetEnd(void){TONY_DWORD_LOCK_CALL(m_TonyDeltaMicroSeconds.GetEnd());}   
  13.     DWORD GetDeltaMicroSeconds(void){TONY_DWORD_LOCK_CALL(m_TonyDeltaMicroSeconds.GetDeltaMicroSeconds());}   
  14.     bool TimeIsUp(const DWORD dwMaxDeltaMicroSeconds,int nModel=2)   
  15.     {TONY_BOOL_LOCK_CALL(m_TonyDeltaMicroSeconds.TimeIsUp(dwMaxDeltaMicroSeconds,nModel));}   
  16. private:   
  17.     CTonyDeltaMicroSeconds m_TonyDeltaMicroSeconds;   
  18.     CMutexLock m_Lock;   
  19. };  
嗯,中間用到幾個宏,是我圖省事,寫的。0bug裡面講過沒有,我忘了,嘿嘿。這裡給出來。 Code:
  1. #define TONY_LOCK_CALL(func) \   
  2. { \   
  3.     m_Lock.Lock(); \   
  4.     func; \   
  5.     m_Lock.Unlock(); \   
  6. }   
  7. #define TONY_DWORD_LOCK_CALL(func) \   
  8. { \   
  9.     DWORD dwRet; \   
  10.     m_Lock.Lock(); \   
  11.     dwRet=func; \   
  12.     m_Lock.Unlock(); \   
  13.     return dwRet; \   
  14. }   
  15. #define TONY_BOOL_READ_LOCK_CALL(func) \   
  16. { \   
  17.     bool bRet; \   
  18.     m_Lock.AddRead(); \   
  19.     bRet=func; \   
  20.     m_Lock.DecRead(); \   
  21.     return bRet; \   
  22. }  
差不多就這麼多吧,內容有點多,大家慢慢看哈。呵呵。 沒辦法,有那麼多細節要考慮,我也寫了整整一個下午呢。本程序在VS2008下測試通過。不過,沒有經過長時間運行,這段代碼我不承諾0bug的。大家注意哈。 =======================================================
在線底價購買《0bug-C/C++商用工程之道》
直接點擊下面鏈接或拷貝到浏覽器地址欄)
http://s.click.taobao.com/t_3?&p=mm_13866629_0_0&n=23&l=http%3A%2F%2Fsearch8.taobao.com%2Fbrowse%2F0%2Fn-g%2Corvv64tborsvwmjvgawdkmbqgboq---g%2Cgaqge5lhebbs6qzlfmqmttgtyo42jm6m22xllqa-------------1%2C2%2C3%2C4%2C5%2C6%2C7%2C8%2C9%2C10%2C11%2C12%2C13%2C14%2C15%2C16%2C17%2C18%2C19%2C20---40--coefp-0-all-0.htm%3Fpid%3Dmm_13866629_0_0 肖舸

本文出自 “肖舸的blog” 博客,請務必保留此出處http://tonyxiaohome.blog.51cto.com/925273/306682

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