1. 程式人生 > >定時器和多執行緒的不同

定時器和多執行緒的不同

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