C++ SetTimer 多媒體計時器和多線程計時器的使用 SetTimer的使用
軟件定時器和多線程在控制工程中有着非常廣泛的使用,主要是因爲在控制過程中,會出現大量的Socket通信和串口通信數據量,仔細想了想,覺得這兩樣東西還是有比較的價值的,很多初學者(我也是。。。)可能會在這兩樣東西上困惑,現簡單比較一下。
首先注意: 線程消息隊列中WM_PAINT,WM_TIMER只有在Queue中沒有其他消息的時候纔會被處理,WM_PAINT消息還會被合併以提高效率。其他所有消息以先進先出(FIFO)的方式被處理。http://zhidao.baidu.com/question/176892832.html?fr=ala0。
1 軟件定時器
很多同學在工程中喜歡使用軟件定時器,因爲其使用簡單,僅需設置一個時長和其OnTime事件即可使用。確實,軟件定時器在某些持續性不強的重複性工作中效率還是不錯的,但是也有着很大的缺點。
缺點1,速度:軟件定時器的精度比較低,這是由Windows不實時的特性所決定的,在XP下,如果關閉所有能關閉的進程,MFC的軟件定時器可以達到接近15ms的精度,而在Win2000下,其能達到接近10ms的精度。但是實際情況是,有些進程是不可以關閉的,比如說數據庫服務器,所以MFC的軟件定時器能夠達到的精度一般情況下在40ms左右,BCB和delphi就更差一點,大概在55ms左右。QueryPerformanceCounter倒是可以大幅提高精度,但是穩定性欠佳。
缺點2,效率:軟件定時器其本質實際上是在消息循環中處理WM_TIMER消息,而WM_TIMER消息在消息隊列中是一個低級別的消息,所以定時器並不能完全保證處理時間間隔的準確性。另外,Timer佔用的是主線程的資源,看似並行實際上是串行,所以窗體的消息隊列一旦堵塞,就會造成系統假死或者運行緩慢,這對於UI來說幾乎是無法忍受的。
2 多線程
多線程技術是在控制工程中常用的技術,因爲在閉環系統中有着大量的數據處理,這些處理顯然不可能放在主線程中處理,絕大多數都是在線程中使用。多線程的優點比較明顯,就是把費勁的東西扔到後臺去,而且對CPU的利用率比較高。如果控制的好,多線程幾乎是沒有什麼缺點的,但實際上控制的好的並不多……原因如下:
1、時間片不可控,搶CPU資源的事情~一般人說不清;
2、同步比較複雜,容易發生死鎖,3條線程同步一般就能把人折騰死。同步我比較喜歡用臨界區,原因也很簡單:因爲臨界區比較簡單……
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1.軟件開發當中經常需要用到這兩個好東東,但是兩個使用起來是有很大區別的哦,
如果在PC上可能效果差不多,如果放到CE小手持設備可能就很明顯的感覺得到;
如果是定時器,如果是在有界面處理的APP中,你會感覺到程序在一頓一頓的;
當然,如果處理的東西本來就很少很少,用兩者是沒有感覺的,但是用在很大的
耗時處理上面,效果就出來了;
爲什麼呢?因爲Timer來了優先級很高所以會先去處理定時器例程,如果處理很
耗時,那一定會一頓一頓的;
Thread就不同了,CE也是搶佔式OS,多線程時是時間輪片處理的;所以如果用線程
的話也可以達到定時器的效果,並且不會感覺到一頓一頓的BUG;因爲無論你的處理
有多耗時,時間片一到就又去處理別的了;如果處理的內容很獨立,沒有與其他
線程有耦合的話,是可以這樣做的;
2.
SetTimer函數和WM_TIMER消息是Win32 api中最基本的玩意兒了,任何初學Win32 api編程的人都應該對此很熟悉吧。在這篇文章中,讓我們來深入瞭解一下和SetTimer相關的使用和應用。
UINT_PTR SetTimer(
HWND hWnd,
UINT_PTR nIDEvent,
UINT uElapse,
TIMERPROC lpTimerFunc
);
我們經常使用的情況是hWnd不爲NULL,lpTimerFunc爲NULL,在這種情況下系統每隔nIDEvent毫秒會向hWnd窗口投遞WM_TIMER消息。唯一需要注意的是:
1.自2000起,uElapse範圍是USER_TIMER_MINIMUM到USER_TIMER_MAXIMUM。超出得話,uElapse設置爲1。
2.WM_TIMER消息其實是在DispatchMessage函數中直接調用hWnd的窗口過程,並且優先級很低,只有在消息隊列中沒有其它消息的情況下,DispatchMessage纔會考慮WM_TIMER。
3.使用相同的nIDEvent可以重置這個Timer,並且KillTimer(hWnd,nIDEvent)來銷燬這個Timer。
我們再來考慮hWnd爲NULL的情況:
1.首先,最重要的是KillTimer時,傳入的Timer Id必須是SetTimer的返回值,而不是調用SetTimer時傳入的nIDEvent參數。
2.調用SetTimer時,如果nIDEvent爲0或者是其它沒有被使用的Timer Id,則SetTimer會返回一個新的Timer Id。否則,就是重新設置這個Timer。
3.如果有lpTimerFunc的話,則lpTimerFunc的參數nIDEvent是SetTimer返回的值,而不是你調用SetTimer時傳入的值。
最後看一下lpTimerFunc不爲NULL的情況:lpTimerFunc會在DispatchMessage函數中被直接調用,而不會去調用hWnd的窗口過程(也就是說收不到這個消息),無論hWnd是不是NULL。(這裏,msdn中貌似有點問題,SetTimer的Remark部分說lpTimerFunc會在默認窗口中被調用,而WM_TIMER中說lpTimerFunc在DispatchMessage中被調用)
應用
使用lpTimerFunc可以做一個延時的操作,或者把某些操作推遲到下一個消息循環,而不需要爲窗口定義一個新的Timer Id。
例如,我很喜歡這樣寫:
struct _DATA
{
//....
};
void CALLBACK TimerProc(HWND hwnd,
UINT uMsg,
UINT_PTR idEvent,
DWORD dwTime)
{
_DATA * data = (_DATA*)idEvent;
KillTimer(hwnd,idEvent);
//do something
free(data);
}
_DATA * data = (_DATA*)malloc(sizeof(_DATA));
SetTimer(AfxGetMainWindow()->m_hWnd,(UINT_PTR)data,10,&TimerProc);
首先,使用了TimerProc,不會使窗口收到WM_TIMER消息,那樣可以使用idEvent來傳遞自定義數據而不會和窗口自己使用的Timer id衝突。
其次,第一個參數hWnd不能爲NULL,否則TimerProc的idEvent參數就不是你傳入的自定義數據了。
最後,msdn說SetTimer不能跨線程使用,所以最好不要用這樣的方法在向ui線程來插入代碼,還是老老實實的發消息吧。