新的公司接手的第一份工作就是一個多線程計算的小系統。也幸虧最近對線程有了一些學習,這次一接手就起到了作用。但是在實際的開發過程中還是發現了許多的問題,比如掛起與終止的概念都沒有弄明白,導致浪費許多的時間。
TThread-簡單的開始
在Delphi的VCL中封裝了一個TThread類用於多線程的開發,這樣比較符合面向對象的思想,同時又可以提高開發效率,一般的情況下開發都是通過派生這個類來實現多線程。所以重點還在這個類TThread上:
簡單的看一眼,這個類倒也簡單,就是封裝了線程的API,通過一個ThreadProc函數來完成了多線程整個過程。很容易就能派生一個線程類:
TMyThread = class(TThread) private FCount: Integer; FOnShowValue: TGetStrProc; protected procedure Execute; override; public constructor Create; property Count: Integer read FCount; property OnShowValue: TGetStrProc read FOnShowValue write FOnShowValue; end; { TMyThread } constructor TMyThread.Create; begin inherited Create(False); FCount := 0; FreeOnTerminate := False; end; procedure TMyThread.Execute; begin while not self.Terminated do begin Inc(FCount); if Assigned(FOnShowValue) then FOnShowValue(IntToStr(FCount)); Sleep(100); end; end;
代碼中只覆蓋了一個Execute方法即可,其他的代碼都是業務相關的代碼,還是非常簡單好用。
線程掛起
線程還支持掛起的功能,即讓CPU將線程中斷,保留現場,不再分配時間片,這樣線程就像死了一般,直到再次喚醒線程再恢復現場繼續執行。
線程終止
在Delphi的TThread類實現中,可以通過一個Terminate方法來讓線程終止。但事實上Terminated只是一個標識而已,在線程啟動時這個標識為False。
線程釋放
一般線程創建后運行完會自動釋放,所以這里的類里我設置FreeOnTerminate := False;,這樣線程對象就不會自動釋放,這樣做的好處就是可以由線程對象以外的代碼來管理線程的生命周期,如果有的代碼需要控制線程對象的生命周期就可以用這個屬性來讓線程不自己釋放。
一般線程創建后運行完會自動釋放,所以這里的類里我設置FreeOnTerminate := False;,這樣線程對象就不會自動釋放,這樣做的好處就是可以由線程對象以外的代碼來管理線程的生命周期,如果有的代碼需要控制線程對象的生命周期就可以用這個屬性來讓線程不自己釋放。
ThreadProc-源代碼分析
function ThreadProc(Thread: TThread): Integer; var FreeThread: Boolean; begin {$IFDEF LINUX} if Thread.FSuspended then sem_wait(Thread.FCreateSuspendedSem); {$ENDIF} try if not Thread.Terminated then try Thread.Execute; except Thread.FFatalException := AcquireExceptionObject; end; finally FreeThread := Thread.FFreeOnTerminate; Result := Thread.FReturnValue; Thread.FFinished := True; Thread.DoTerminate; if FreeThread then Thread.Free; {$IFDEF MSWINDOWS} EndThread(Result); {$ENDIF} {$IFDEF LINUX} // Directly call pthread_exit since EndThread will detach the thread causing // the pthread_join in TThread.WaitFor to fail. Also, make sure the EndThreadProc // is called just like EndThread would do. EndThreadProc should not return // and call pthread_exit itself. if Assigned(EndThreadProc) then EndThreadProc(Result); pthread_exit(Pointer(Result)); {$ENDIF} end; end;
對於TThread的一個關鍵部分就是這個ThreadProc方法,它是線程創建時傳給系統API的回調函數;Delphi中通過這個方法完成了一個核心的功能,可以看到代碼中調用了Execute方法。這也就是為什么派生類只要覆寫這個方法的原因。
所以從代碼也可以看出,線程啟動后代碼是順序執行的,代碼走完就結束了,所以為了讓線程能夠一直在運行就要在Execute方法里加上一個死循環,保證線程一直在運算,直到接收到Terminated時才讓線程結束掉。所以Terminated的真正作用在這呢,需要開發者自己來控制,當然這樣也就變的非常靈活了。