Reputation: 629
At the moment I'm using additional thread to nicely free memory after thread. Before you ask. No I can't use FreeOnTerminate:=true because I need .waitfor. I also need FreeAndNil() because only in this way I can check if thread is running using Assigned(). Example code.
procedure TForm1.Button1Click(Sender: TObject);
begin
SupervisorThread:= TSupervisorThread.Create(True);
SupervisorThread.FreeOnTerminate:=false; //MUST BE FALSE!
SupervisorThread.Priority := tpNormal;
SupervisorThread.Resume;
end;
procedure TSupervisorThread.Execute;
begin
CleaningThread:= TCleaningThread.Create(True);
CleaningThread.FreeOnTerminate:=true;
CleaningThread.Priority := tpNormal;
CleaningThread.Resume;
//some loops here
end;
procedure TCleaningThread.Execute;
begin
if Assigned(SupervisorThread)=true then
begin
SupervisorThread.WaitFor;
FreeAndNil(SupervisorThread);
end;
end;
procedure TForm2.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if Assigned(SupervisorThread)=false then CanClose:=true
else
begin
CanClose:=false;
ShowMessage('Cannot close form because SiupervisorThread is still working');
end;
end;
Upvotes: 4
Views: 1085
Reputation: 595742
You can use the TThread.OnTerminate
event to detect when a thread has finished running, eg:
procedure TForm1.Button1Click(Sender: TObject);
begin
if not Assigned(SupervisorThread) then
begin
SupervisorThread:= TSupervisorThread.Create(True);
SupervisorThread.FreeOnTerminate := False;
SupervisorThread.Priority := tpNormal;
SupervisorThread.OnTerminate := SupervisorThreadTerminated;
SupervisorThread.Resume;
end;
end;
procedure TForm1.SupervisorThreadTerminated(Sender: TObject);
begin
SupervisorThread := nil;
end;
However, this creates some problems. It creates a race condition, since the cleaning thread acts on the SupervisorThread
pointer, which could disappear at any time while the cleaning thread is still running. And it creates a memory leak, as you still need to free the SupervisorThread
object after it has terminated, but you can't do that in the OnTerminate
handler directly.
A better solution would not rely on the SupervisorThread
pointer at all.
var
SupervisorTerminated: TEvent;
procedure TForm1.FormCreate(Sender: TObject);
begin
SupervisorTerminated := TEvent.Create(nil, True, True, '');
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
if Assigned(SupervisorThread) then
begin
SupervisorThread.Terminate;
while SupervisorTerminated.WaitFor(1000) = wrTimeout do
CheckSynchronize;
end;
SupervisorTerminated.Free;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
if not Assigned(SupervisorThread) then
begin
SupervisorThread := TSupervisorThread.Create(True);
SupervisorThread.FreeOnTerminate := True;
SupervisorThread.Priority := tpNormal;
SupervisorThread.OnTerminate := SupervisorThreadTerminated;
SupervisorTerminated.ResetEvent;
SupervisorThread.Resume;
end;
end;
procedure TForm1.SupervisorThreadTerminated(Sender: TObject);
begin
SupervisorThread := nil;
SupervisorTerminated.SetEvent;
end;
procedure TCleaningThread.Execute;
begin
SupervisorTerminated.WaitFor(INFINITE);
end;
procedure TForm2.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
CanClose := (SupervisorTerminated.WaitFor(0) = wrSignaled);
if not CanClose then
ShowMessage('Cannot close form because Supervisor Thread is still working');
end;
Upvotes: 1
Reputation:
Use the TThread.OnTerminate
event:
private
procedure DoTerminateEvent(Sender: TObject);
var
isRunning: Boolean;
procedure TForm2.DoTerminateEvent(Sender: TObject);
begin
isRunning := False;
end;
procedure TForm2.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if (isRunning) then
begin
CanClose := false;
ShowMessage('Cannot close form because SupervisorThread is still working')
end else
CanClose := true;
end;
Set the OnTerminate
handler when creating the Thread:
SupervisorThread := TSupervisorThread.Create(True);
...
SupervisorThread.OnTerminate := DoTerminateEvent;
SupervisorThread.Resume;
Or, pass it as a parameter to the Thread's constructor:
TSupervisorThread = class(TThread)
public
constructor Create(OnTerminatEvent: TNotifyEvent);
end;
procedure TThreadCustom.Create(OnTerminateEvent: TNotifyEvent);
begin
inherited Create(True);
OnTerminate := OnTerminateEvent;
end;
SupervisorThread := TSupervisorThread.Create(DoTerminateEvent);
Upvotes: 4