在动态包库中使用TTask时内存泄漏

时间:2016-06-23 11:29:16

标签: delphi

在动态包中使用TTask会导致调试器中的内存泄漏和访问冲突。在某些情况下,似乎TTask不适合在动态打包应用程序中使用。

我在delphi包中定义了一个外部过程:

unit LibUnit;

interface

implementation

uses System.Threading, Winapi.Windows;

procedure Test;
begin
  TTask.Run(procedure begin OutputDebugString(PChar('Task Executing')); end);
end;

exports Test;

end.

然后我在我的主应用程序中使用该过程:

uses
  System.SysUtils,
  System.Threading,
  Winapi.Windows;

var H: THandle;
    P: procedure;
begin
  ReportMemoryLeaksOnShutdown := True;

  H := LoadPackage('MyPackage.bpl');
  @P := GetProcAddress(H, PChar('Test'));
  P;
  Sleep(1000);
  OutputDebugString(PChar('Start unload package'));
  UnloadPackage(H);

  ReadLn;
end.

应用程序关闭后提示内存泄漏:

This application has leaked memory. The small block leaks are (excluding expected leaks registered by pointer):

13 - 20 bytes: Unknown x 1
21 - 36 bytes: TThreadPool.TControlFlag x 1
69 - 84 bytes: TTask x 1

示例项目可以从RSP-15316

下载

我注意到这是由于在卸载软件包后在TTask exit中创建的新线程:

Thread Start: Thread ID: 8308. Process MainProject.exe (6208)
Debug Output: Task Executing Process MainProject.exe (6208)
Debug Output: Start unload package Process MainProject.exe (6208)
Module Unload: MyPackage.bpl. Process MainProject.exe (6208)
Debug Output: Thread Exiting: 8308 Process MainProject.exe (6208) message 'access violation at 0x50067528: read of address 0x02351fb8'. Process MainProject.exe (6208)
Thread Exit: Thread ID: 8308. Process MainProject.exe (6208)

如果我们可以在线程退出后找到控制包卸载的方法,那么内存泄漏就不会再发生了。

1 个答案:

答案 0 :(得分:0)

我找到了一种解决方法。不要使用ThreadPool类本身管理的默认TThreadPool实例。相反,定义自己的TThreadPool实例并在TTask中使用:

unit LibUnit;

interface

implementation

uses System.Threading, Winapi.Windows;

var ThreadPool: TThreadPool;

procedure Test;
begin
  TTask.Run(procedure begin OutputDebugString(PChar('Task Executing')); end, ThreadPool);
end;

exports Test;

initialization
  ThreadPool := TThreadPool.Create;
finalization
  ThreadPool.DisposeOf;
end.