How to automatically execute FreeAndNill () after thread termination

I am currently using an additional thread for beautifully free memory after the thread. Before you ask. No. I cannot use FreeOnTerminate: = true because I need .waitfor. I also need FreeAndNil (), because only in this way can I check if the stream is working with Assigned (). Sample 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; 
+5
source share
2 answers

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; 

Install the OnTerminate constructor when creating the stream:

 SupervisorThread := TSupervisorThread.Create(True); ... SupervisorThread.OnTerminate := DoTerminateEvent; SupervisorThread.Resume; 

Or pass it as a parameter to the Thread 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); 
+4
source

You can use the TThread.OnTerminate event to determine when a thread has completed, for example:

 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. This creates a race condition since the cleaning thread acts on the SupervisorThread pointer, which can disappear at any time while the cleaning thread is still running. And this creates a memory leak, because you still need to free the SupervisorThread object after it finishes, but you cannot do it directly in the OnTerminate handler.

A better solution would not rely on a 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; 
+1
source

Source: https://habr.com/ru/post/1266236/


All Articles