Подтвердить что ты не робот

Thread.FreeOnTerminate: = True, утечка памяти и запуск призрака

Несколько лет назад я решил никогда не полагаться только на установку свойства FreeOnTerminate в true, чтобы быть уверенным в его уничтожении, потому что Я обнаружил и аргументировал две вещи при завершении приложения:

  • он вызывает утечку памяти и
  • после завершения программы поток по-прежнему работает где-то ниже клавиатуры моего ноутбука.

Я познакомился с обходным решением, и это не беспокоило меня все это время. До сегодняшнего дня, когда снова кто-то (@MartinJames в этом случае) прокомментировал мой ответ, в котором я ссылаюсь на некоторый код, который не использует FreeOnTerminate в сочетании с преждевременным завершением потока. Я вернулся в RTL-код и понял, что, возможно, сделал неправильные предположения. Но я не совсем уверен в этом, поэтому этот вопрос.

Во-первых, чтобы воспроизвести вышеупомянутые утверждения, этот иллюстративный код используется:

unit Unit3;

interface

uses
  Classes, Windows, Messages, Forms;

type
  TMyThread = class(TThread)
    FForm: TForm;
    procedure Progress;
    procedure Execute; override;
  end;

  TMainForm = class(TForm)
    procedure FormClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FThread: TMyThread;
  end;

implementation

{$R *.dfm}

{ TMyThread }

procedure TMyThread.Execute;
begin
  while not Terminated do
  begin
    Synchronize(Progress);
    Sleep(2000);
  end;
end;

procedure TMyThread.Progress;
begin
  FForm.Caption := FForm.Caption + '.';
end;

{ TMainForm }

procedure TMainForm.FormClick(Sender: TObject);
begin
  FThread := TMyThread.Create(True);
  FThread.FForm := Self;
  FThread.FreeOnTerminate := True;
  FThread.Resume;
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  FThread.Terminate;
end;

end.

Теперь (ситуация A), если вы запустите поток с щелчком по форме и закроете форму сразу после изменения заголовка, происходит утечка памяти из 68 байтов. Я предполагаю, что это потому, что поток не освобожден. Во-вторых, программа немедленно завершается, и среда IDE снова возвращается в нормальное состояние. Это в отличие от (ситуация B): когда не используется FreeOnTerminate, и последняя строка вышеуказанного кода изменяется на FThread.Free, она принимает (макс.) 2 секунды от исчезновения программы до нормальной IDE состояние.

Задержка в ситуации B объясняется тем, что FThread.Free вызывает FThread.WaitFor, оба из которых выполняются в контексте основного потока. Дальнейшее исследование Classes.pas узнало, что уничтожение потока из-за FreeOnTerminate выполняется в контексте рабочего потока. Это приводит к следующим вопросам о ситуации A:

  • Есть ли утечка памяти? И если да: важно ли это, можно ли его игнорировать? Потому что, когда приложение завершается, Windows не возвращает все зарезервированные ресурсы?
  • Что происходит с потоком? Действительно ли он работает где-то в памяти до тех пор, пока его работа не будет выполнена, или нет? И: освобождается ли он, несмотря на доказательства утечки памяти?

Отказ от ответственности: для обнаружения утечки памяти я использую этот очень простой блок как первый в файле проекта.

4b9b3361

Ответ 1

В самом деле, ОС восстанавливает всю память процесса, когда она завершается, поэтому даже если эти 68 байтов относятся к незанятому потоку объекта, ОС все равно вернет эти байты. На самом деле не имеет значения, освободил ли вы объект в этой точке.

Когда ваша основная программа заканчивается, она в конечном итоге достигает места, где она вызывает ExitProcess. (Вы должны иметь возможность включить отладочные DCU в своих опциях компоновщика проекта и перейти к этой точке с помощью отладчика.) Этот вызов API выполняет несколько действий, включая завершение всех остальных потоков. Нити не сообщаются, что они завершаются, поэтому код очистки, предоставляемый TThread, никогда не запускается. Поток ОС просто перестает существовать.