何时手动释放线程

时间:2011-11-24 06:01:45

标签: delphi delphi-xe2

如果我从主线程中创建一个(暂停的)线程:

  with TMyThread.Create(True) do
  begin
    OnTerminate := ThreadTerminated;
    FreeOnTerminate := False;
    Start;
  end;

如果该实例完成后如何释放该实例? (即执行过程已完成执行 - 假设我已捕获异常)。

Proper way of destroying a tthread object链接显示了一种方法(通过PostMessage过程),该方法工作正常且有意义。但是,如果我创建线程并且我没有表单的句柄或我可以调用PostMessage过程的东西,该怎么办?例如,我在直接来自TObject的类中创建线程?

TMyClass = class
public
  procedure DoSomething;
end;

TMyClass.DoSomething;
begin
      with TMyThread.Create(True) do
      begin
        OnTerminate := ThreadTerminated;
        FreeOnTerminate := False;
        Start;
      end;  
end;

所以,我想,如何在不访问表单句柄的情况下释放一个线程?

由于

1 个答案:

答案 0 :(得分:7)

显然,某处必须有对实例化线程的引用。但我可以提出你的愿望:你想要一个永远不做的解决方案。

我建议你通过一个单独的ThreadController类来管理线程的存在:

unit Unit2;

interface

uses
  Classes, SysUtils, Forms, Windows, Messages;

type
  TMyThreadProgressEvent = procedure(Value: Integer;
    Proceed: Boolean) of object;

procedure RunMyThread(StartValue: Integer; OnProgress: TMyThreadProgressEvent);

implementation

type
  TMyThread = class(TThread)
  private
    FException: Exception;
    FOnProgress: TMyThreadProgressEvent;
    FProceed: Boolean;
    FValue: Integer;
    procedure DoProgress;
    procedure HandleException;
    procedure ShowException;
  protected
    procedure Execute; override;
  end;

  TMyThreadController = class(TObject)
  private
    FThreads: TList;
    procedure StartThread(StartValue: Integer;
      OnProgress: TMyThreadProgressEvent);
    procedure ThreadTerminate(Sender: TObject);
  public
    constructor Create;
    destructor Destroy; override;
  end;

var
  FMyThreadController: TMyThreadController;

function MyThreadController: TMyThreadController;
begin
  if not Assigned(FMyThreadController) then
    FMyThreadController := TMyThreadController.Create;
  Result := FMyThreadController
end;

procedure RunMyThread(StartValue: Integer; OnProgress: TMyThreadProgressEvent);
begin
  MyThreadController.StartThread(StartValue, OnProgress);
end;

{ TMyThreadController }

constructor TMyThreadController.Create;
begin
  inherited;
  FThreads := TList.Create;
end;

destructor TMyThreadController.Destroy;
var
  Thread: TThread;
begin
  while FThreads.Count > 0 do
  begin
    Thread := FThreads[0]; //Save reference because Terminate indirectly
                           //extracts the list entry in OnTerminate!
    Thread.Terminate; //Indirectly decreases FThreads.Count
    Thread.Free;
  end;
  FThreads.Free;
  inherited Destroy;
end;

procedure TMyThreadController.StartThread(StartValue: Integer;
  OnProgress: TMyThreadProgressEvent);
var
  Thread: TMyThread;
begin
  Thread := TMyThread.Create(True);
  FThreads.Add(Thread); //Add to list before a call to Resume because once
                        //resumed, the thread might be gone already!
  Thread.FValue := StartValue;
  Thread.FOnProgress := OnProgress;
  Thread.OnTerminate := ThreadTerminate;
  Thread.Resume;
end;

procedure TMyThreadController.ThreadTerminate(Sender: TObject);
begin
  FThreads.Extract(Sender);
end;

{ TMyThread }

procedure TMyThread.DoProgress;
begin
  if (not Application.Terminated) and Assigned(FOnProgress) then
    FOnProgress(FValue, FProceed);
end;

procedure TMyThread.Execute;
begin
  try
    FProceed := True;
    while (not Terminated) and (not Application.Terminated) and FProceed and
      (FValue < 20) do
    begin
      Synchronize(DoProgress);
      if not FProceed then
        Break;
      Inc(FValue);
      Sleep(2000);
    end;
    //In case of normal execution ending, the thread may free itself. Otherwise,
    //the thread controller object frees the thread.
    if not Terminated then
      FreeOnTerminate := True;
  except
    HandleException;
  end;
end;

procedure TMyThread.HandleException;
begin
  FException := Exception(ExceptObject);
  try
    if not (FException is EAbort) then
      Synchronize(ShowException);
  finally
    FException := nil;
  end;
end;

procedure TMyThread.ShowException;
begin
  if GetCapture <> 0 then
    SendMessage(GetCapture, WM_CANCELMODE, 0, 0);
  if (FException is Exception) and (not Application.Terminated) then
    Application.ShowException(FException)
  else
    SysUtils.ShowException(FException, nil);
end;

initialization

finalization
  FreeAndNil(FMyThreadController);

end.

要运行此示例线程,以2秒的间隔从5到19计数,并提供反馈和提前终止的机会,请从主线程调用:

type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    procedure MyThreadProgress(Value: Integer; Proceed: Boolean);
  end;

...

procedure TForm1.Button1Click(Sender: TObject);
begin
  RunMyThread(5, MyThreadProgress);
end;

procedure TForm1.MyThreadProgress(Value: Integer; Proceed: Boolean);
begin
  Caption := IntToStr(Value);
end;

此线程会自动终止线程或应用程序的终止。

也许这个单位对你的情况有点过分,因为它能够处理多个线程(相同类型),但我认为它可以回答你的问题。根据自己的喜好调整。

这个答案的部分来源:NLDelphi.com