如何在应用程序关闭时终止Delphi中的匿名线程?

Ive*_*aev 8 delphi multithreading anonymous terminate delphi-xe2

我有一个Delphi应用程序,它在一些TTimer.OnTimer事件中产生了6个匿名线程.

如果我从标题栏中的X按钮关闭应用程序,则会引发地址$ C0000005处的访问冲突,并且FastMM报告泄漏的TAnonymousThread对象.

哪个是使用TThread.CreateAnonymousThread()方法在OnTimer事件中创建的Delphi中释放匿名线程的最佳方法?

解决方案对我有用:

创建了一个匿名线程的包装器,它在被Free-ed时终止它们.

type
  TAnonumousThreadPool = class sealed(TObject)
  strict private
    FThreadList: TThreadList;
    procedure TerminateRunningThreads;
    procedure AnonumousThreadTerminate(Sender: TObject);
  public
    destructor Destroy; override; final;
    procedure Start(const Procs: array of TProc);
  end;

{ TAnonumousThreadPool }

procedure TAnonumousThreadPool.Start(const Procs: array of TProc);
var
  T: TThread;
  n: Integer;
begin
  TerminateRunningThreads;

  FThreadList := TThreadList.Create;
  FThreadList.Duplicates := TDuplicates.dupError;

  for n := Low(Procs) to High(Procs) do
  begin
    T := TThread.CreateAnonymousThread(Procs[n]);
    TThread.NameThreadForDebugging(AnsiString('Test thread N:' + IntToStr(n) + ' TID:'), T.ThreadID);
    T.OnTerminate := AnonumousThreadTerminate;
    T.FreeOnTerminate := true;
    FThreadList.LockList;
    try
      FThreadList.Add(T);
    finally
      FThreadList.UnlockList;
    end;
    T.Start;
  end;
end;

procedure TAnonumousThreadPool.AnonumousThreadTerminate(Sender: TObject);
begin
  FThreadList.LockList;
  try
    FThreadList.Remove((Sender as TThread));
  finally
    FThreadList.UnlockList;
  end;
end;

procedure TAnonumousThreadPool.TerminateRunningThreads;
var
  L: TList;
  T: TThread;
begin
  if not Assigned(FThreadList) then
    Exit;
  L := FThreadList.LockList;
  try
    while L.Count > 0 do
    begin
      T := TThread(L[0]);
      T.OnTerminate := nil;
      L.Remove(L[0]);
      T.FreeOnTerminate := False;
      T.Terminate;
      T.Free;
    end;
  finally
    FThreadList.UnlockList;
  end;
  FThreadList.Free;
end;

destructor TAnonumousThreadPool.Destroy;
begin
  TerminateRunningThreads;
  inherited;
end;
Run Code Online (Sandbox Code Playgroud)

在这里结束是你如何称呼它:

procedure TForm1.Button1Click(Sender: TObject);
begin
  FAnonymousThreadPool.Start([ // array of procedures to execute
    procedure{anonymous1}()
    var
      Http: THttpClient;
    begin
      Http := THttpClient.Create;
      try
        Http.CancelledCallback := function: Boolean
          begin
            Result := TThread.CurrentThread.CheckTerminated;
          end;
        Http.GetFile('http://mtgstudio.com/Screenshots/shot1.png', 'c:\1.jpg');
      finally
        Http.Free;
      end;
    end,

    procedure{anonymous2}()
    var
      Http: THttpClient;
    begin
      Http := THttpClient.Create;
      try
        Http.CancelledCallback := function: Boolean
          begin
            Result := TThread.CurrentThread.CheckTerminated;
          end;
        Http.GetFile('http://mtgstudio.com/Screenshots/shot2.png', 'c:\2.jpg');
      finally
        Http.Free;
      end;
    end
  ]);
end;
Run Code Online (Sandbox Code Playgroud)

没有内存泄漏,正常关闭和易于使用.

Dav*_*nan 15

如果你想维持并控制线程的生命周期,那么它必须FreeOnTerminate设置为False.否则,在线程开始执行后引用它是错误的.那是因为一旦它开始执行,你就没有现成的方法来知道它是否已被释放.

调用CreateAnonymousThread创建一个FreeOnTerminate设置为的线程True.

该线程也标记为FreeOnTerminate,因此在调用Start后不应触摸返回的实例.

所以,但默认情况下,您无法控制线程的生命周期.但是,您可以设置FreeOnTerminateFalse调用之前立即Start.像这样:

MyThread := TThread.CreateAnonymousThread(MyProc);
MyThread.FreeOnTerminate := False;
MyThread.Start;
Run Code Online (Sandbox Code Playgroud)

但是,我不确定我会这样做.设计CreateAnonymousThread是在终止时自动释放线程.我想我个人会按照预期的设计,或者得出我自己的TThread后代.

  • 你可以控制匿名线程,因为它是一个正常的TThread它我刚刚初始化为FreeOnTerminate ...正如我在我的answear中发布的那样.因此,您应该编辑您的answear并删除"您不应该使用匿名线程"的部分 (2认同)

Die*_*cia 9

在启动之前使用CreateAnonymousThreadjust set FreeOnTerminate来避免错误False.

通过这种方式,您可以像平常一样使用线程,而无需任何解决方法.

您可以阅读CreateAnonymousThread自动设置FreeOnTerminate为的文档,True这是引用该线程时导致错误的原因.