Как прекратить анонимные потоки в Delphi при закрытии приложения?

у меня есть приложение Delphi, которое порождает 6 анонимных потоков на некотором TTimer.Событие OnTimer.

Если я закрою приложение от кнопки X в заголовке нарушение доступа по адресу $C0000005 поднимается и fastmm отчеты просочились TAnonymousThread объектов.

это лучший способ освободить анонимные потоки в Delphi, созданные в событии OnTimer с помощью TThread.CreateAnonymousThread() метод?

решение, которое работало для я:

создал оболочку анонимных потоков, которая завершает их при свободном редактировании.

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;

конец вот как вы можете это назвать:

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:.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:.jpg');
      finally
        Http.Free;
      end;
    end
  ]);
end;

отсутствие утечек памяти, правильное выключение и простота в использовании.

3 ответов


если вы хотите поддерживать и контролировать время жизни потока, то он должен иметь FreeOnTerminate значение False. В противном случае ссылка на поток после его запуска является ошибкой. Это потому, что как только он начнет выполняться, у вас нет готового способа узнать, был ли он освобожден.

вызов CreateAnonymousThread создает поток с FreeOnTerminate значение True.

поток также отмечен как FreeOnTerminate, поэтому вы не следует прикасаться к возвращаемому экземпляру после вызова Start.

и так, но по умолчанию вы не в состоянии контролировать время жизни потока. Тем не менее, вы можете установить FreeOnTerminate to False непосредственно перед вызовом Start. Вот так:

MyThread := TThread.CreateAnonymousThread(MyProc);
MyThread.FreeOnTerminate := False;
MyThread.Start;

однако я не уверен, что сделал бы это. Дизайн CreateAnonymousThread является то, что поток автоматически освобождается после завершения. Я думаю, что лично я бы либо следовал намеченному дизайну, либо получил мой собственный TThread descendent.


чтобы избежать ошибок с помощью CreateAnonymousThread просто набор FreeOnTerminate to False прежде чем начать его.

таким образом, вы можете работать с потоком, как обычно, без каких-либо обходных путей.

вы можете прочитать документацию, которая говорит, что CreateAnonymousThread автоматически FreeOnTerminate to True и это то, что вызывает ошибки при ссылке на поток.


Заставьте свои потоки следить за каким-то уведомлением извне. Это может быть событие, которое получает сигнал, сообщение, отправленное в окно, принадлежащее потоку, команда, отправленная через сокет, который слушает ваш поток, или любая другая форма связи, которую вы найдете.

Если вы определяете, что эта проблема заключается в том, что ваши потоки являются так называемыми "анонимными" потоками, то простой обходной путь-сделать их не анонимными потоками. Положите тело анонима функция в Execute метод и передать любые захваченные переменные в класс thread через его конструктор.