標籤:
首先看TThread源碼中關於OnTerminate的代碼:
public .... property OnTerminate: TNotifyEvent read FOnTerminate write FOnTerminate; ...end;
再看Delphi內建的協助手冊中對於OnTerminate的解釋:
Occurs after the thread‘s Execute method has returned and before the thread is destroyed.
property OnTerminate: TNotifyEvent;
Description
Write an OnTerminate event handler to execute code after the thread finishes executing. The OnTerminate event handler is called in the context of the main thread, which means CLX methods and properties can be called freely.
翻譯成中文的大致的解釋就是:線程的該屬性如果被賦值為一個方法,那麼此方法將會在該線程的Execute方法執行完成並退出,但是線程還沒有被釋放之前執行。
一個實際的例子:
unit Unit1;interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;type TForm1 = class(TForm) btn1: TButton; procedure btn1Click(Sender: TObject); private procedure ThreadDone(Sender: TObject); public { Public declarations } end; TTestThread = class(TThread) protected procedure Execute; override; end;var Form1: TForm1;implementation{$R *.dfm}const MaxSize = 128;var NextNumber: Integer = 0; DoneFlags: Integer = 0; GlobalArray: array[1..MaxSize] of Integer; CS: TRTLCriticalSection; function GetNextNumber: Integer; begin Result:= NextNumber; Inc(NextNumber); end; procedure TTestThread.Execute; var i: Integer; begin OnTerminate:= Form1.ThreadDone; //在這裡設定OnTerminate屬性為Form1的ThreadDone方法,表示線上程執行完Execute之後,還沒有被釋放之前,要緊接著執行Form1的ThreadDone方法。 EnterCriticalSection(CS); for i:= 1 to MaxSize do begin GlobalArray[i]:= GetNextNumber; Sleep(5); end; LeaveCriticalSection(CS); end; procedure TForm1.ThreadDone(Sender: TObject); var i: Integer; begin inc(DoneFlags); if DoneFlags = 2 then begin for i:= 1 to MaxSize do lst1.Items.Add(IntToStr(GlobalArray[i])); DeleteCriticalSection(CS); end; end;procedure TForm1.btn1Click(Sender: TObject);begin initializeCriticalSection(CS); TTestThread.Create(False); TTestThread.Create(False);end;end.
先解釋OnTerminate:在這裡設定OnTerminate屬性為Form1的ThreadDone方法,表示線上程執行完Execute之後,還沒有被釋放之前,要緊接著執行Form1的ThreadDone方法。
再對線程的同步進行說明:這裡使用臨界區進行線程的同步,該常式會在點擊按鈕之後開啟兩個線程。
每個線程都是對一個全域數組GlobalArray進行操作,使用了臨界區的話,(看TForm1的btn1Click方法)雖然是連續開啟兩個線程,但是因為使用了臨界區,而且是線上程的Execute中進行操作之前先進入臨界區,所以當一個線程運行(也就是執行Execute方法)時,第一個線程進入臨界區對全域數組GlobalArray進行性操作,那麼另外一個線程就只能等待,等到第一個線程完成操作並且離開臨界區之後(此時GlobalArray數組的值是從0到127)。第二個線程開始進入臨界區,並且開始對GlobalArray進行操作,最後GlobalArray數組中的值是從128到255。
通過使用臨界區就可以保證多個線程不能同時對一個資源進行操作,而如果沒有使用臨界區對線程進行同步的話,那麼最後可能是線程1、線程2隨機連續對數組進行操作(因為作業系統對線程的時間片的分配是隨機的)(還有NextNumber這個全域變數也會被兩個線程隨機連續操作,可能造成衝突),最後的結果就是當兩個線程都執行完成之後,GlobalArray數組中的值沒有任何規律,所以這時候就不可能根據規則來控制線程,這時候開發出來的程式就是開發人員所不能控制的,顯然不利於程式的穩定性等。
所以在多個線程會去操作同一個資源等情況的時候,必須對線程進行同步,保證線程的可控性。
Delphi多線程的OnTerminate屬性(附加一個關於臨界區線程同步的例子)