這應該算我近期的工作了。我最近在做一個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:
- class CDWORDCount
- {
- public:
- CDWORDCount(){Reset();}
- ~CDWORDCount(){}
- DWORD Reset(DWORD dwValue=0){return m_dwBegin=m_dwEnd=dwValue;}
- DWORD SetBegin(DWORD dwValue){return (m_dwBegin=dwValue);}
- DWORD SetEnd(DWORD dwValue){return m_dwEnd=dwValue;}
- DWORD GetBegin(void){return m_dwBegin;}
- DWORD GetEnd(void){return m_dwEnd;}
- DWORD GetX(void)
- {
- if(m_dwEnd<m_dwBegin)
- {
- //處理溢出問題
- int nBeginX=abs(0-m_dwBegin); //求得m_dwBegin與0之間的差值nBeginX
- return m_dwEnd+(DWORD)nBeginX; //m_dwEnd此時的值是相對0之間的溢出部分
- //真實的差值是nBeginX與m_dwEnd之和
- }
- return (m_dwEnd-m_dwBegin);
- }
- private:
- DWORD m_dwBegin;
- DWORD m_dwEnd;
- };
大家注意GetX這個函數,是求Begin和End的差值,由於windows下,求毫秒的函數GetTickCount返回的是DWORD,因此,我這個工具類也主要處理DWORD這個類型。具體函數意思我不用寫注釋了吧,看英文都看得懂。
當然,按我團隊的規矩,還是要給一個測試函數,大家可以看看其對溢出邏輯的處理是正確的:
Code:
- inline void TestCDWORDCount(void)
- {
- CDWORDCount Count;
- Count.SetBegin(0xFFFFFFFF);
- Count.SetEnd(5); //此時的End相當於0x100000005
- printf("%ld\n",Count.GetX());
- printf("%ld\n",abs(0-0xFFFFFFFF));
- }
ok,有了這個工具,我准備開始寫計時器,我先完成了基本部分,嗯,大家注意,為了保證精度,這裡面全部使用的是C++的內聯函數寫法,即函數會在編譯時在調用處展開,節約壓棧、彈棧的動作開銷,我們這是在計時,省一點總是好的。
先給大家一個宏吧,這是求毫秒級精度的系統函數的跨平台封裝。《0bug-C/C++商用工程之道》裡面有,各位讀者可以看看。
Code:
- #define _GetTimeOfDay GetTickCount
由於寫這個計時器一波三折,我也是分幾次才寫好,因此,這裡先給基本類,後面再逐漸介紹幾套計時方案。
Code:
- class CTonyDeltaMicroSeconds
- {
- public:
- CTonyDeltaMicroSeconds(){Reset();}
- ~CTonyDeltaMicroSeconds(){}
- void Reset(void)
- {
- m_Count.Reset(_GetTimeOfDay());
- m_nFix=0; //這個回頭再說
- m_Mean.Reset(); //這個回頭再說
- }
- void PrintInfo(void)
- {
- printf("m_nFix=%d\n",m_nFix);
- m_Mean.PrintInfo();
- }
- DWORD TouchBegin(void){return m_Count.SetBegin(_GetTimeOfDay());}
- DWORD TouchEnd(void){return m_Count.SetEnd(_GetTimeOfDay());}
- DWORD GetBegin(void){return m_Count.GetBegin();}
- DWORD GetEnd(void){return m_Count.GetEnd();}
- DWORD GetDeltaMicroSeconds(void){return m_Count.GetX();}
- private:
- CDWORDCount m_Count;
- CTonyMean m_Mean; //這個回頭再說
- int m_nFix; //這個回頭再說
- };
大家注意,其實基本類很簡單,都在調用前一個類,僅僅是把基數綁定在系統取得的毫秒數上。
然後,我開始考慮怎麼樣計時,其實基本原理很簡單,我內部保存了Begin和End,兩次時間點的毫秒差,就是內部的DeltaTime,這個和外部傳進來的時間段MaxDelta比較一下,如果內部DeltaTime>外部時差,則表示時間到了,可以返回真,否則返回假,所以,我寫了第一個計時函數TimeIsUp0。
Code:
- bool TimeIsUp0(const DWORD dwMaxDeltaMicroSeconds)
- {
- //直接比較內部的時間差和外部的判斷時長,
- //如果外部的判斷時長<=內部時間差,則返回真
- //無修訂,精度誤差15ms左右
- return (dwMaxDeltaMicroSeconds<=GetDeltaMicroSeconds());
- }
簡單吧,呵呵。
可是效果不好,我測了一下,其精度誤差在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:
- bool TimeIsUp1(const DWORD dwMaxDeltaMicroSeconds)
- {
- //初級修訂,每次判斷時,外部條件需要加上一個修訂值再判斷
- //以這個修訂值來抵消誤差
- //dwMaxDeltaMicroSeconds=100,nDeltaT=110,表示外部程序循環精度不高,110ms才訪問一次本接口
- //則nDeltaT-dwMaxDeltaMicroSeconds=10>0,修訂值-1初值為0)
- //下次判斷時,dwMaxDeltaMicroSeconds=100+(-1)=99,
- //這使得nCompNumber更容易<=nDeltaT,也就是說,更快地返回時間到的真標志
- //dwMaxDeltaMicroSeconds=100,nDeltaT=90,表示外部程序循環精度太高,90ms就訪問一次本接口
- //則nDeltaT-dwMaxDeltaMicroSeconds=-10<0,修訂值+1初值為0)
- //下次判斷時,dwMaxDeltaMicroSeconds=100+(1)=101,
- //這使得nCompNumber更不容易<=nDeltaT,也就是說,更慢地返回時間到的真標志
- //修訂值總是在跳變,當出現一次為真的成功比較,即時間到,
- //此時nDeltaT大約相當於外部要求的dwMaxDeltaMicroSeconds,
- //二者之間的差值nDeltaT-nMaxDeltaMicroSeconds即作為修訂值的調整依據
- //調整後的修訂值,影響下面幾輪的比較動作
- //由於這是使用絕對差值nDeltaT-nMaxDeltaMicroSeconds在調整修訂值m_nFix,
- //導致m_nFix的抖動很大,誤差還是比較大,經實測,誤差在+-6~7ms左右,
- //即100ms的求精,得到94~106左右的范圍,誤差還是很大
- int nDeltaT=(int)GetDeltaMicroSeconds(); //獲得Begin和End之間的時間差
- int nMaxDeltaMicroSeconds=(int)dwMaxDeltaMicroSeconds; //業務判斷的毫秒數取整型
- int nCompNumber=nMaxDeltaMicroSeconds+m_nFix; //業務判斷毫秒數和修正值累加,獲得比較值
- bool bRet=(nCompNumber<=nDeltaT); //比較值和內部保存的時間差比較
- if(bRet)
- {
- //如果為真,表示時間到,調整修正值
- int nFixX=nDeltaT-nMaxDeltaMicroSeconds; //取得修訂系數,實際存儲的時間差減去外部給定的差值
- if(0<nFixX) m_nFix--; //如果修訂系數為負數,表示nMaxDeltaMicroSeconds太大,
- //修正值-1,下次的nCompNumber就會小1
- else if(0>nFixX) m_nFix++; //否則,修正值+1,下次的nCompNumber就會大1
- //這種修訂方法取絕對值,m_nFix抖動很大,精度不高
- }
- return bRet;
- }
不過出來一測,發現精度還是太低,有改善,我獲得了7~8ms的誤差精度,即我要1ms,得到7~8ms,要100ms,得到93~107ms的誤差范圍。
我仔細分析了一下,推測可能是直接使用DeltaTime-MaxDelta的絕對值來做判定,動作太大了,m_nFix跳變抖動得厲害,影響了精度。
那,有個簡單的辦法來實現這個邏輯,就是使用多次DeltaTime-MaxDelta的平均值來影響m_nFix,減小抖動。不過,這帶來一個問題,我手邊還沒有一個合用的平均值計算模塊,想了一下,我干脆寫了一個。
Code:
- #include <float.h>
- //判定一個double數是否為0.0,相當於if(0.0==dValue)
- inline bool DoubleIsZero(double dValue){return (abs(dValue)<DBL_EPSILON);}
- //判定兩個double數是否絕對相等,相當於if(dA==dB)
- inline bool DoubleIsEqual(double dA,double dB){return DoubleIsZero(dA-dB);}
- class CTonyMean
- {
- public:
- CTonyMean(){Reset();}
- ~CTonyMean(){}
- void Reset(void)
- {
- m_dAllCount=0.0;
- m_nTimes=0;
- m_dMean=0.0;
- m_nOnlyPositiveNumberFlag=0;
- m_nOnlyNegativeNumberFlag=0;
- }
- double Push(double dValue)
- {
- //printf("Push(%f)\n",dValue); //調試代碼,可以刪除
- //統計是否只處理單一數據種類正數或者負數)
- if(0>dValue) m_nOnlyPositiveNumberFlag=1;
- if(0<dValue) m_nOnlyNegativeNumberFlag=1;
- m_nTimes++;
- //由於本類沒有限定輸入的dValue值是否為正數或者
- //因此m_dAllCount可能會由於單一數種溢出導致為0.0,
- //也可能因為正負數抵消為0.0
- //必須分別處理
- m_dAllCount+=dValue;
- //如果據統計,有史以來只使用純正數,或者純負數,
- //則處理總累加器溢出
- //否則則認為是正負數抵消的0.0,無需處理
- if(OnlyOneTypeNumber())
- if(DoubleIsZero(m_dAllCount))
- ResetByOverflow();
- //由於m_nTimes總是先+1後使用,
- //因此,此處的m_nTimes==0一定是溢出
- //使用溢出處理方案
- if(!m_nTimes) ResetByOverflow();
- //計算平均值
- m_dMean=m_dAllCount/(double)(m_nTimes);
- return GetMean();
- }
- //友好接口,大多數時候,大家在用整數做值,因此,允許直接輸入整數值
- double Push(int nValue){return Push((double)nValue);}
- //求得平均數
- double GetMean(void){return m_dMean;}
- //內部打印
- void PrintInfo(void)
- {
- printf("CTonyMean: AllCount=%f, Times=%d, Mean=%f\n",
- m_dAllCount,m_nTimes,m_dMean);
- printf("Positive=%d, Negative=%d, OnlyOneTypeNumber()=%d\n",
- m_nOnlyPositiveNumberFlag,
- m_nOnlyNegativeNumberFlag,
- OnlyOneTypeNumber());
- }
- private:
- //是否是相同類型的數字判斷
- int OnlyOneTypeNumber(void)
- {return (m_nOnlyPositiveNumberFlag^m_nOnlyNegativeNumberFlag);}
- //溢出處理方案,設置次數為1,
- //設置總累計數為以前求的平均值
- //二者相除,平均值不變
- //以便保留以往的平均值繼續計算結果。
- void ResetByOverflow(void)
- {
- //把總累加器恢復為上一輪的平均值
- m_dAllCount=m_dMean;
- //m_nTimes無條件設置為1
- m_nTimes=1;
- }
- unsigned int m_nTimes; //總次數
- double m_dAllCount; //總累加器
- double m_dMean; //平均值
- unsigned int m_nOnlyPositiveNumberFlag; //有史以來,存儲過正數標志
- unsigned int m_nOnlyNegativeNumberFlag; //有史以來,存儲過負數標志
- };
- inline void TestCTonyMean(void)
- {
- CTonyMean Mean;
-
- Mean.Reset();
- Mean.Push(1);
- Mean.Push(2);
- Mean.PrintInfo();
-
- Mean.Reset();
- Mean.Push(-1);
- Mean.Push(-2);
- Mean.PrintInfo();
-
-
- Mean.Reset();
- Mean.Push(-1);
- Mean.Push(2);
- Mean.PrintInfo();
- }
我想我注釋挺明白的,就不多說了吧。大家現在知道計時器裡面的CTonyMean m_Mean;怎麼來的了吧。呵呵。
嗯,這段代碼調整過,原來的老代碼,只能處理純正數或者有正負數,現在調整為可以同時處理純正數,正負數,以及純負數三種情況,這樣適用面更廣一點。嗯,我順手加了個測試代碼,大家有興趣可以run起來看看。
然後,我寫了第三段精確計時邏輯。
Code:
- bool TimeIsUp2(const DWORD dwMaxDeltaMicroSeconds)
- {
- //高級修訂,利用平均值來優化m_nFix的算法,降低抖動
- //計算原理同TimeIsUp1
- //每次不再以nDeltaT-nMaxDeltaMicroSeconds作為m_nFix的修訂依據
- //而是以多次nDeltaT-nMaxDeltaMicroSeconds的平均值,作為m_nFix的修訂依據
- //經實測,m_nFix的抖動較小,獲得+-0.003ms的左右時間精度
- //即100ms,獲得99.997ms~100.003ms的時間精度,1ms求精,可以得到0.997~1.003ms的計時精度
- int nDeltaT=(int)GetDeltaMicroSeconds(); //獲得Begin和End之間的時間差
- int nMaxDeltaMicroSeconds=(int)dwMaxDeltaMicroSeconds; //業務判斷的毫秒數取整型
- int nCompNumber=nMaxDeltaMicroSeconds+m_nFix; //業務判斷毫秒數和修正值累加,獲得比較值
- bool bRet=(nCompNumber<=nDeltaT); //比較值和內部保存的時間差比較
- if(bRet)
- {
- m_Mean.Push(nDeltaT-nMaxDeltaMicroSeconds); //此處計算平均值
- if(0.0<m_Mean.GetMean()) m_nFix--; //以平均值的正負性來調整修訂值
- else if(0.0>m_Mean.GetMean()) m_nFix++;
-
- }
- return bRet;
- }
大家請注意平均值計算的代入。
這段代碼效果很好,我測試了一下,獲得了+-0.003ms左右的精度。
當然,我想了一下,干脆,這些個代碼全部提供,所以我寫了個集中的函數,利用switch選擇用哪種計時精度。
Code:
- bool TimeIsUp(const DWORD dwMaxDeltaMicroSeconds,int nModel=2)
- {
- bool bRet=false;
- TouchEnd();
- switch(nModel)
- {
- default: //其他數取默認值
- case 0: //普通模式
- bRet=TimeIsUp0(dwMaxDeltaMicroSeconds);
- break;
- case 1: //加入了誤差修訂
- bRet=TimeIsUp1(dwMaxDeltaMicroSeconds);
- break;
- case 2: //加入了平均值誤差修訂
- bRet=TimeIsUp2(dwMaxDeltaMicroSeconds);
- break;
- }
- return bRet;
- }
嗯,這是判定邏輯,有時候,業務層可能不想自己做個循環頻繁判定,因此需要這個邏輯自己提供一個等待睡眠的函數,我也寫了一個。
Code:
- void Wait4(const DWORD dwMaxDeltaMicroSeconds,int nModel=2)
- {while(!TimeIsUp(dwMaxDeltaMicroSeconds,nModel)){TonyXiaoMinSleep();}}
嗯,沒有看過《0bug-C/C++商用工程之道》這本書的朋友,可能看不懂TonyXiaoMinSleep();這個函數,那簡單,直接用Sleep0)代替好了。
最後,我寫了一段代碼測試一下,由於這裡面有動態跟蹤邏輯,因此,前面幾次測試肯定不准,需要工作一段時間,m_nFix調整得差不多了,精度就高了,因此,我寫了如下邏輯。每種模式,0、1、2三種,分別測試5輪,每輪測試1000次,取平均值,來看測試的精度。
精度測試很簡單,我用個計數器,每次疊加DeltaTime這個模塊內部的Delta值,最後除以總的訪問次數,就得到平均每次的毫秒數。
Code:
- #define TestCDeltaMicroSeconds_MAX_COUNT 1000
- inline void TestCDeltaMicroSeconds(void)
- {
- CTonyMean TimeMeadPreModel;
- CTonyDeltaMicroSeconds dms;
- int i=0;
- int j=0;
- int k=0;
- int nCount=0;
- double dMS=0.0;
- for(i=0;i<3;i++)
- {
- TimeMeadPreModel.Reset();
- for(j=0;j<5;j++)
- {
- printf("Test: model=%d, try=%d, test %d times pre try\n",i,j,TestCDeltaMicroSeconds_MAX_COUNT);
- nCount=0;
- dms.Reset();
- for(k=0;k<TestCDeltaMicroSeconds_MAX_COUNT;k++)
- {
- dms.TouchBegin();
- //while(!dms.TimeIsUp(1,i)){}//{Sleep(10);}
- dms.Wait4(1,i);
- nCount+=dms.GetDeltaMicroSeconds();
- }
- dMS=(double)nCount/(double)TestCDeltaMicroSeconds_MAX_COUNT;
- TimeMeadPreModel.Push(dMS);
- printf("Model %d, try=%d %d times: Mean= %f ms\n",i,j,TestCDeltaMicroSeconds_MAX_COUNT,dMS);
- printf("-----------\n");
- }
- printf("Model %d Mean: %f ms\n",i,TimeMeadPreModel.GetMean());
- printf("=====================================\n");
- }
- }
運行結果如下:
Code:
- Test: model=0, try=0, test 1000 times pre try
- Model 0, try=0 1000 times: Mean= 15.625000 ms
- -----------
- Test: model=0, try=1, test 1000 times pre try
- Model 0, try=1 1000 times: Mean= 15.625000 ms
- -----------
- Test: model=0, try=2, test 1000 times pre try
- Model 0, try=2 1000 times: Mean= 15.625000 ms
- -----------
- Test: model=0, try=3, test 1000 times pre try
- Model 0, try=3 1000 times: Mean= 15.625000 ms
- -----------
- Test: model=0, try=4, test 1000 times pre try
- Model 0, try=4 1000 times: Mean= 15.625000 ms
- -----------
- Model 0 Mean: 15.625000 ms //第一種模式,差不多15.625ms的精度
- =====================================
- Test: model=1, try=0, test 1000 times pre try
- Model 1, try=0 1000 times: Mean= 7.813000 ms
- -----------
- Test: model=1, try=1, test 1000 times pre try
- Model 1, try=1 1000 times: Mean= 7.812000 ms
- -----------
- Test: model=1, try=2, test 1000 times pre try
- Model 1, try=2 1000 times: Mean= 7.813000 ms
- -----------
- Test: model=1, try=3, test 1000 times pre try
- Model 1, try=3 1000 times: Mean= 7.812000 ms
- -----------
- Test: model=1, try=4, test 1000 times pre try
- Model 1, try=4 1000 times: Mean= 7.813000 ms
- -----------
- Model 1 Mean: 7.812600 ms //第二種模式,差不多7.8ms的精度
- =====================================
- Test: model=2, try=0, test 1000 times pre try
- Model 2, try=0 1000 times: Mean= 1.000000 ms
- -----------
- Test: model=2, try=1, test 1000 times pre try
- Model 2, try=1 1000 times: Mean= 1.000000 ms
- -----------
- Test: model=2, try=2, test 1000 times pre try
- Model 2, try=2 1000 times: Mean= 1.000000 ms
- -----------
- Test: model=2, try=3, test 1000 times pre try
- Model 2, try=3 1000 times: Mean= 1.000000 ms
- -----------
- Test: model=2, try=4, test 1000 times pre try
- Model 2, try=4 1000 times: Mean= 1.000000 ms
- -----------
- Model 2 Mean: 1.000000 ms //第三種模式,精確計量出1ms
- =====================================
呵呵,當然,由於這個模塊本意是要跨線程工作,我這裡給出它的資源鎖封裝版本。
Code:
- class CTonyDeltaMicroSecondsWithLock
- {
- public:
- CTonyDeltaMicroSecondsWithLock(){}
- ~CTonyDeltaMicroSecondsWithLock(){}
- public:
- void Reset(void){TONY_LOCK_CALL(m_TonyDeltaMicroSeconds.Reset())}
- void PrintInfo(void){TONY_LOCK_CALL(m_TonyDeltaMicroSeconds.PrintInfo());}
- DWORD TouchBegin(void){TONY_DWORD_LOCK_CALL(m_TonyDeltaMicroSeconds.TouchBegin());}
- DWORD TouchEnd(void){TONY_DWORD_LOCK_CALL(m_TonyDeltaMicroSeconds.TouchEnd());}
- DWORD GetBegin(void){TONY_DWORD_LOCK_CALL(m_TonyDeltaMicroSeconds.GetBegin());}
- DWORD GetEnd(void){TONY_DWORD_LOCK_CALL(m_TonyDeltaMicroSeconds.GetEnd());}
- DWORD GetDeltaMicroSeconds(void){TONY_DWORD_LOCK_CALL(m_TonyDeltaMicroSeconds.GetDeltaMicroSeconds());}
- bool TimeIsUp(const DWORD dwMaxDeltaMicroSeconds,int nModel=2)
- {TONY_BOOL_LOCK_CALL(m_TonyDeltaMicroSeconds.TimeIsUp(dwMaxDeltaMicroSeconds,nModel));}
- private:
- CTonyDeltaMicroSeconds m_TonyDeltaMicroSeconds;
- CMutexLock m_Lock;
- };
嗯,中間用到幾個宏,是我圖省事,寫的。0bug裡面講過沒有,我忘了,嘿嘿。這裡給出來。
Code:
- #define TONY_LOCK_CALL(func) \
- { \
- m_Lock.Lock(); \
- func; \
- m_Lock.Unlock(); \
- }
- #define TONY_DWORD_LOCK_CALL(func) \
- { \
- DWORD dwRet; \
- m_Lock.Lock(); \
- dwRet=func; \
- m_Lock.Unlock(); \
- return dwRet; \
- }
- #define TONY_BOOL_READ_LOCK_CALL(func) \
- { \
- bool bRet; \
- m_Lock.AddRead(); \
- bRet=func; \
- m_Lock.DecRead(); \
- return bRet; \
- }
差不多就這麼多吧,內容有點多,大家慢慢看哈。呵呵。
沒辦法,有那麼多細節要考慮,我也寫了整整一個下午呢。本程序在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