定時器和多執行緒的不同
軟體定時器和多執行緒在控制工程中有著非常廣泛的使用,主要是因為在控制過程中,會出現大量的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佔用的是主執行緒的
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執行緒來插入程式碼,還是老老實實的發訊息吧。