Thread.FreeOnTerminate:= True,内存泄漏和ghost运行

NGL*_*GLN 13 delphi multithreading memory-leaks delphi-7

几年前,我决定永远不要仅仅依赖于将线程的FreeOnTerminate属性设置为true以确保其被破坏,因为我在应用程序终止时发现并推理了两件事:

  1. 它会产生内存泄漏,并且
  2. 程序终止后,线程仍然在笔记本键盘下方的某处运行.

我熟悉了一个解决方法,并没有一直困扰我.直到今晚,再次有人(在这种情况下为@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.
Run Code Online (Sandbox Code Playgroud)

现在(情况A),如果你单击表单启动线程,并在标题更改后立即关闭表单,则会有68字节的内存泄漏.我认为这是因为线程没有被释放.其次,程序立即终止,IDE在同一时刻再次恢复正常状态.与(情况B)相反:当不使用FreeOnTerminate并且上述代码的最后一行被更改为时FThread.Free,从程序消失到正常IDE状态需要(最多)2秒.

情况B的延迟是由FThread.Free调用这一事实解释的FThread.WaitFor,这两个调用都是在主线程的上下文中执行的.对Classes.pas的进一步研究了解到,由于FreeOnTerminate在工作线程的上下文中完成了对线程的破坏.这导致了关于情况A的以下问题:

  • 确实有内存泄漏吗?如果是这样:重要的是,它可以被忽略吗?因为当应用程序终止时,Windows是否会回放所有保留的资源?
  • 线程会发生什么?它确实在内存中的某个地方运行,直到它的工作完成,或不是吗?并且:它是否被释放,尽管有内存泄漏的证据?

免责声明:对于内存泄漏检测,我在项目文件中首先使用这个非常简单的单元.

Rob*_*edy 12

实际上,操作系统在终止时回收所有进程的内存,所以即使这68个字节引用了非释放的线程对象,操作系统仍将取回这些字节.你是否在那时释放了对象并不重要.

当您的主程序完成时,它最终会到达它调用的地方ExitProcess.(您应该能够在项目的链接器选项中打开调试DCU,然后使用调试器逐步调试.)该API调用会执行多项操作,包括终止所有其他线程.线程不会被通知他们正在终止,因此提供的清理代码TThread永远不会运行.OS线程根本不存在.