多年前,我决定永远不要仅仅依赖于将线程的FreeOnTerminate
属性设置为true以确保其被破坏,因为我在应用程序终止时发现并推理了两件事:
我熟悉了一个解决方法,并没有一直困扰我。直到今晚,当有人(在这种情况下为@MartinJames)评论my answer时,我引用了一些不使用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的以下问题:
免责声明:对于内存泄漏检测,我在项目文件中首先使用this very simple unit。
答案 0 :(得分:12)
实际上,操作系统在终止时回收所有进程的内存,因此即使这68个字节引用了未释放的线程对象,操作系统仍将取回这些字节。你是否在那时释放了对象并不重要。
当你的主程序结束时,它最终会到达一个调用ExitProcess
的地方。 (您应该能够在项目的链接器选项中打开调试DCU,然后使用调试器逐步调试。)该API调用会执行多项操作,包括终止所有其他线程。线程未被通知它们正在终止,因此TThread
提供的清理代码永远不会运行。操作系统线程不再存在。