Много лет назад я решил никогда не полагаться только на установку свойства 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
, то от исчезновения программы до нормального состояния IDE проходит (максимум) 2 секунды.
Задержка в ситуации B объясняется тем, что FThread.Free
вызывает FThread.WaitFor
, оба из которых выполняются в контексте главного потока. При дальнейшем исследовании Classes.pas выяснилось, что уничтожение потока из-за FreeOnTerminate
происходит в контексте рабочего потока. Это привело к следующим вопросам по ситуации A:
Отказ от ответственности: Для обнаружения утечки памяти я использую этот очень простой блок, как первый в файле проекта.