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

时间:2012-01-27 06:28:10

标签: multithreading delphi memory-leaks delphi-7

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

  1. 会产生内存泄漏,
  2. 程序终止后,线程仍在笔记本键盘下方的某处运行。
  3. 我熟悉了一个解决方法,并没有一直困扰我。直到今晚,当有人(在这种情况下为@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的以下问题:

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

    免责声明:对于内存泄漏检测,我在项目文件中首先使用this very simple unit

1 个答案:

答案 0 :(得分:12)

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

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