用C#的Thread做了一個簡單計時器。為了讓自己45分鐘後就可以休息一次,45分鐘過後會響音樂提示。
開始使用的TimeSpan相減的方式,在Thread的啟動函數中也就是這樣寫的:
public void CountTime(){ while (true) { TimeSpan tsNew = new TimeSpan(DateTime.Now.Ticks); TimeSpan tsIn = tsNew - tsOld; if (tsIn.Minutes >= 1) { while (true) { TimeSpan tsNewer = new TimeSpan(DateTime.Now.Ticks); TimeSpan tsIner = tsNewer - tsNew; if (tsIner.Minutes >= 10) { //十分鐘後線程重啟 tsOld = tsNew; break; } } } }}
後來發現這種方法效率太低了。當然,可以使用Thread.Sleep(20);這個函數降低CPU佔用時間。其實中間加入Thread.Sleep(20);就可明顯的降低CPU消耗。後來發現C#中的Thread中內建有函數join(),這個函數可以使線程等待一段時間。調用方法如下
th.Join(new TimeSpan(hours, minutes, seconds));在等待的這段時間裡線程處於WaitSleepJoin狀態。
當然也可以調用Thread.Sleep(millionseconds);這裡提一下Sleep和Join的區別
當線程執行Sleep時系統就退出執行隊列一段時間,當睡眠結束時,系統會產生一個時鐘中斷,從而使線程回到執行隊列中恢複線程的執行。Sleep方法如果參數是0,代表這個線程應當掛起讓其他等待的線程運行,這裡cpu回重新分配控制權,也有可能是剛才的執行的程式。這樣就會有cpu佔用總是100%的情況發生。有時你介面死了,但是你還是可以移動滑鼠。如果是Timeout.Infinite,就代表將使線程休眠,直到它被調用 Thread.Interrupt 的另一個線程中斷或被 Thread.Abort 中止為止。
如果父線程先於子線程結束,那麼子線程將在父線程結束的同時被迫結束。Thread.Join()方法使父線程等待,直到子線程結束。Join方法有傳回值,當值為true,代表線程終止。false的話代表線程在等待了那段時間後還未終止。如果線上程Unstarted狀態時,調用join()就會有ThreadStateException異常。如果線程已經終止,那麼調用這個函數,會立即得到傳回值。
例如下面的主程式
...ThreadStart st = New ThreadStart(fun);Thread th = new Thread(ThreadStart st);th.Start();Application.Exit();... //下面是fun函數void fun(){ while(true) { ... }}
這段程式的唯一毛病就是有可能在主程式退出後,從線程還沒有結束。(這個從線程真可憐...)
這裡順便再提一下線程的幾個狀態:
建立:當建立一個新進程時,也為該進程建立了一個線程。線程還可以建立新線程。
就緒:線程已獲得除處理機外的所有資源。
運行:線程正在處理機上執行。
阻塞:線程因等待某事件而暫停運行。
終止:一個線程已完成。
但是C#的線程中多了幾個狀態:
Aborted,AbortRequested,Background,Running,Stopped,StopRequested,Suspended,SuspendRequested,Unstarted,WaitSleepJoin。
Abort()將導致ThreadState.AbortRequested調用Abort()的線程獲得控制權之後導致ThreadState.Aborted,AbortRequested與Aborted的區別在於一個停止一個未停止。而Stopped則是線程終止。但是我反覆實驗多次發現當調用Abort()函數後,線程狀態會變為Stopped。如何變為Aborted狀態,還在研究中。其他幾個狀態差不多。都是調用相應的函數會產生相應的狀態請求,還有過一段時間才能到底相應的狀態。至於Unstarted是還未調用Start()函數,Running是調用Start()或者Resume()函數的結果。WaitSleepJoin是在等待I/O,或者是調用Join()方法。這裡Join()和Sleep()方法的不同還在於調用Join()線程狀態進入WaitSleepJoin,調用Sleep()線程狀態還是Running。
掛起線程的方法是Suspend();調用這個方法後,線程處於SuspendRequest狀態。Suspended()調用後如果線程仍然在執行join()方法,因為Suspended()要讓線程到達安全點之後它才可以將該線程掛起,此時那線程狀態就是SuspendRequested|WaitSleepJoin。但是這裡的join裡的時鐘依然在計數。所以現在還不知道用什麼方法來暫停這個join計數,當然也可以不使用join解決徹底暫停線程這個問題。現在不明白哪個Suspended()函數是幹什麼的,因為線程依舊在運行中。另外值得一提的是現在不提倡使用Suspend()和讓線程調用Suspend()後再次恢複的Resume()方法。原因很簡單,因為這兩個方法是由另外的線程執行,另外的線程並不能準確的知道被Suspend()的線程處於何種狀態,如某個類的建構函式執行時期,或者析構等。所以用這個函數來同步很危險。
另外,要注意的是Thread.Sleep(n)這個n不能精確的控制時間。如果你認為要線程要隔多長時間,這個控制就有問題。如果當前的線程是一個前台線程,那麼Thread.Sleep(n)就要在大於n的時間才能退出。如果是後台進程,當主程式退出後,這線程就再也不能喚醒..悲慘..所以一般也建議不用Thread.Sleep()函數。另外Sleep函數也不能用於同步.peter說程式的Sleep函數代表了一個很爛的設計。