2013-11-25 36 views
0

我一直在試圖設置FreeOnTerminate屬性OnTerminate程序,但它似乎像它要麼爲時已晚來設置它,或者它完全無視write程序。設置/更改TThread.FreeOnTerminate同時TThread.OnTerminate

我該如何設置/更改FreeOnTerminate屬性inOnTerminate程序? 是否有任何解決方法?

一些代碼:

unit Unit2; 

interface 

uses 
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, 
    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; 

type 
    TForm2 = class(TForm) 
    Button1: TButton; 
    Button2: TButton; 
    procedure Button1Click(Sender: TObject); 
    procedure Button2Click(Sender: TObject); 
    private 
    procedure OnTestThreadTerminate (Sender : TObject); 
    public 
    { Public declarations } 
    end; 

type 
    TTestThread = class (TThread) 
    public 
    procedure Execute; override; 
end; 


var 
    Form2: TForm2; 
    GlobalThreadTest : TTestThread; 

implementation 

{$R *.dfm} 

procedure TForm2.Button1Click(Sender: TObject); 
begin 
    GlobalThreadTest     := TTestThread.Create (True); 
    GlobalThreadTest.OnTerminate  := Self.OnTestThreadTerminate; 
    GlobalThreadTest.FreeOnTerminate := True; 
    GlobalThreadTest.Resume; 
end; 

procedure TForm2.Button2Click(Sender: TObject); 
begin 
    // 2nd Button to try to free the thread... 
    // AFTER BUTTON1 has been clicked! 
    try 
    GlobalThreadTest.Free; 
    except 
    on e : exception do begin 
     MessageBox(Self.Handle, pchar(e.Message), pchar(e.ClassName), 64); 
    end; 
    end; 
end; 

procedure TForm2.OnTestThreadTerminate(Sender: TObject); 
begin 
    TTestThread(Sender).FreeOnTerminate := False;      // Avoid freeing... 
    ShowMessage (BoolToStr (TTestThread(Sender).FreeOnTerminate, True)); // FreeOnTerminate Value has been changed successfully! 
end; 

procedure TTestThread.Execute; 
begin 
    // No code needed for test purposes. 
end; 

end. 
+1

您有數據競賽。 一旦您將FreeOnTerminate設置爲True,並致電Resume或 開始,那麼您將不再可靠地參照線索 。 FreeOnTerminate的設計是它被設置爲 真一次,永遠不會被修改。無論你在做什麼 ,你的解決方案都是錯誤的。 –

+1

設置FreeOnTerminate當您希望免除對線程對象的自由調用 。當你使用 時,你不能持有對線程實例的引用。 如果您想要破壞線程 ,請不要設置FreeOnTerminate。 –

+0

@DavidHeffernan我只是不明白爲什麼它不會工作。實現這樣的細節並不是什麼大事。在'OnTerminate'完成後最後一次檢查'FreeOnTerminate'應該很容易...謝謝您的信息。 –

回答

4

如果你看看到的ThreadProc在Classes.pas來源,你會發現,FreeOnTerminate正在調用DoTerminate的OnTerminate事件之前計算爲一個局部變量Freethread
在調用DoTerminate後,根據現在過時的變量釋放線程:if FreeThread then Thread.Free;
您可以在不使用FreeOnTerminate的情況下啓動線程,並使用PostMessage和自己的消息,例如在OnTerminate中調用WM_MyKillMessage(WM_APP + 1234)以在離開OnTerminate事件後釋放線程。

這可能是這樣的:

const 
    WM_KillThread = WM_APP + 1234; 
type 

    TTestThread = class (TThread) 
    public 
    procedure Execute; override; 
    Destructor Destroy;override; 
end; 
    TForm2 = class(TForm) 
    ............... 
    public 
    { Public-Deklarationen } 
    procedure WMKILLTHREAD(var Msg:TMessage);message WM_KillThread; 
    end; 


procedure TForm2.OnTestThread(Sender: TObject); 
begin 
    ShowMessage ('OnTestThread'); 
    PostMessage(handle,WM_KillThread, WPARAM(Sender), 0); 
end; 

procedure TForm2.WMKILLTHREAD(var Msg: TMessage); 
begin 
    TTestThread(Msg.WParam).Free; 
end; 

destructor TTestThread.Destroy; 
begin 
    ShowMessage ('Destroy'); 
    inherited; 
end; 
+0

我真的很喜歡將新消息發佈到隊列的想法,因爲'OnTerminate'在Mainthread。非常感謝你。 –

4

FreeOnTerminate之後Execute()退出評估,但DoTerminate()之前被稱爲觸發OnTerminate事件。您可以更改FreeOnTerminate,直至退出Execute(),然後爲時已晚。因此,一個解決辦法是從Execute()內手動觸發OnTerminate,如:

type 
    TTestThread = class (TThread) 
    public 
    procedure Execute; override; 
    procedure DoTerminate; override; 
    end; 

procedure TTestThread.Execute; 
begin 
    try 
    ... 
    finally 
    // trigger OnTerminate here... 
    inherited DoTerminate; 
    end; 
end; 

procedure TTestThread.DoTerminate; 
begin 
    // prevent TThread from triggering OnTerminate 
    // again by not calling inherited here... 
end; 

唯一的問題在於,假如Terminate()Execute()之前調用被稱爲然後TThread將跳過Execute(),但它仍然會調用覆蓋的DoTerminate()

3

大概你想在某種情況下設置FreeOnTerminate False,但讓它保持爲True。如果這種情況取決於它的終止是否自然(執行結束通常沒有干預)或手動(終止稱爲),那麼我建議你做相反的事情:創建FreeOnTerminate = False的線程,並將Terminated屬性設置爲True是假的:

procedure TTestThread.Execute; 
begin 
    ... 
    if not Terminated then 
    FreeOnTerminate := True; 
end; 

查看其功能,例如在When to free a Thread manually