如何在线程终止后自动执行FreeAndNill()

时间:2017-04-02 19:06:42

标签: delphi

目前我正在使用额外的线程来完美地释放线程后的内存。 在你问之前。不,我不能使用FreeOnTerminate:= true因为我需要.waitfor。 我还需要FreeAndNil(),因为只有这样我才能使用Assigned()检查线程是否正在运行。示例代码。

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;

2 个答案:

答案 0 :(得分:4)

使用TThread.OnTerminate事件:

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;

创建线程时设置OnTerminate处理程序:

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

或者,将它作为参数传递给Thread的构造函数:

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);

答案 1 :(得分:1)

您可以使用TThread.OnTerminate事件来检测线程何时完成运行,例如:

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;

但是,这会产生一些问题。它会创建一个竞争条件,因为清理线程作用于SupervisorThread指针,当清洁线程仍在运行时,该指针可能随时消失。它会造成内存泄漏,因为您仍然需要在SupervisorThread对象终止后释放它,但您无法直接在OnTerminate处理程序中执行此操作。

更好的解决方案根本不依赖于SupervisorThread指针。

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;