1. 程式人生 > >Delphi多執行緒的OnTerminate屬性(附加一個關於臨界區執行緒同步的例子)

Delphi多執行緒的OnTerminate屬性(附加一個關於臨界區執行緒同步的例子)

  首先看TThread原始碼中關於OnTerminate的程式碼:

?
1 2 3 4 5 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方法執行完成並退出,但是執行緒還沒有被釋放之前執行。

  但是這裡只是講到它的使用,而且還可能只是多種可以使用的地方之一,但是OnTerminate是什麼,它的原理是什麼並沒有講到,所以還需要再去鑽研。

  一個實際的例子:

?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 unit Unit1; interface uses 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陣列中的值沒有任何規律,所以這時候就不可能根據規則來控制執行緒,這時候開發出來的程式就是開發者所不能控制的,顯然不利於程式的穩定性等。

               所以在多個執行緒會去操作同一個資源等情況的時候,必須對執行緒進行同步,保證執行緒的可控性。