如何正确地让一个线程自己终止?

时间:2018-04-13 21:56:28

标签: delphi

我有这个帖子:

 TWorkerThread = class(TThread)
  private
    FSignal: TEvent;
  public
    constructor Create;
    destructor Destroy; override;
    procedure Execute; override;
  end;

constructor TWorkerThread.Create;
begin
  FSignal := TEvent.Create(nil, false{ManualReset}, false, '');
  inherited Create(false{CreateSuspended});
end;

destructor TWorkerThread.Destroy;
begin
  Terminate;
  FSignal.SetEvent;
  WaitFor;
  AlFreeAndNil(FSignal);
  inherited;
end;

procedure TWorkerThread.Execute;
begin
  while not Terminated do begin

    ....

    fSignal.WaitFor(INFINITE);

  end;
end;

好的,所以现在"有时"在我的主线程中我想释放这个帖子,但我不想等待它完成(不想阻止主线程),所以我现在不能打电话{ {1}}将执行MyThread.free

我想像这样做一些想法

waitFor

但是这可能会出错,想象一下当我打电话给 fMyWorkerThreads[i].FreeOnTerminate := True; fMyWorkerThreads[i].Terminate; fMyWorkerThreads[i].Signal.SetEvent; fMyWorkerThreads[i] := nil; 时,如果我非常不走运,fMyWorkerThreads[i].Terminate;之前被释放到了fMyWorkerThreads[i]我将会有访问违规?

所以我该怎么办?如果我在fMyWorkerThreads[i].Signal.SetEvent;之前拨打fMyWorkerThreads[i].Signal.SetEvent;,我也可能不幸运行,fMyWorkerThreads[i].Terminate;往返前往fMyWorkerThreads[i].execute再次到达fSignal.WaitFor(INFINITE); < / p>

1 个答案:

答案 0 :(得分:-2)

简单的解决方案是不使用标准Terminate警告:以下代码适用于您的抽象问题。这不是所有案例的模板。

interface

uses
  System.Classes, System.SyncObjs;

type

  TTestThread = class(TThread)
  private
    FSignal: TEvent;
  protected
    procedure Execute; override;
  public
    constructor Create;
    destructor Destroy; override;

    procedure CustomTerminate;
  end;

procedure Test1();
procedure Test2();

implementation

uses
  Winapi.Windows;

{ TTestThread }

constructor TTestThread.Create;
begin
  FSignal := TEvent.Create(nil, True{must be true}, False, '');
  inherited Create(False);
end;

procedure TTestThread.CustomTerminate;
begin
  FSignal.SetEvent();
end;

destructor TTestThread.Destroy;
begin
  CustomTerminate();

  // this is standart way, but it will be much better to use one more own event
  if not FreeOnTerminate then // without this check, will be deadlock
    WaitFor;

  FSignal.Free;
  inherited;
end;

procedure TTestThread.Execute;
begin
  // TEvent.WaitFor can end with an error. Read docs before use
  while FSignal.WaitFor(0) = TWaitResult.wrTimeout do begin
    FSignal.WaitFor(INFINITE);
  end;
end;

procedure Test1();
var
  T: TTestThread;
begin
  T := TTestThread.Create();
  try
    Sleep(2000);
  finally
    T.Free;
  end;
end;

procedure Test2();
var
  T: TTestThread;
begin
  T := TTestThread.Create();
  Sleep(2000);
  T.FreeOnTerminate := True;
  T.CustomTerminate;
end;

<强>可是...

你想在线程类之外使用FreeOnTerminate,它可以很方便,但它引入了危险的混淆。在构造函数中使用此标志要好得多,或者根本不使用它。以下是您不应该使用它的更多原因:

  • 如果你不是很小心,一个被遗忘的线程可以使用不应该
  • 的数据
  • 如果线程在应用程序停止之前没有停止,则可能会出现一些错误
  • 如果线程意外终止(例如异常),之后设置FreeOnTerminate,则线程对象不会被销毁。

恕我直言:我不建议使用FreeOnTerminate

<强> UPD: 使用标准Terminate的解决方案。

interface

uses
  System.Classes, System.SyncObjs;

type

  TTestThread = class(TThread)
  private
    FSignal: TEvent;
  protected
    procedure Execute; override;
    procedure TerminatedSet; override;
  public
    constructor Create;
    destructor Destroy; override;
  end;

implementation

uses
  Winapi.Windows;

{ TTestThread }

constructor TTestThread.Create;
begin
  FSignal := TEvent.Create(nil, True, False, '');
  inherited Create(False);
end;

destructor TTestThread.Destroy;
begin
  Terminate;

  // this is standart way, but it will be much better to use one more own event
  if not FreeOnTerminate then // without this check, will be deadlock
    WaitFor;

  FSignal.Free;
  inherited;
end;

procedure TTestThread.Execute;
begin
  // TEvent.WaitFor can end with an error. Read docs before use
  while FSignal.WaitFor(0) = TWaitResult.wrTimeout do begin
    FSignal.WaitFor(INFINITE);
  end;
end;

procedure TTestThread.TerminatedSet;
begin
  FSignal.SetEvent();
end;

end;