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

Delphi 在动态包库中使用TTask时内存泄漏,delphi,Delphi,在动态包中使用TTask将导致调试器中的内存泄漏和访问冲突。在某些情况下,TTask似乎不适合在动态打包应用程序中使用 我在delphi包中定义了一个外部过程: unit LibUnit; interface implementation uses System.Threading, Winapi.Windows; procedure Test; begin TTask.Run(procedure begin OutputDebugString(PChar('Task Executi

在动态包中使用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
示例项目可以从

我注意到这是由于卸载包后在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)

如果我们能找到一种方法来控制线程退出后的包卸载,那么内存泄漏将不会再发生。

我找到了一个解决方案。不要使用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.

我找到了一个解决办法。不要使用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.

当您在调用P和卸载包之间放置一些等待时,是否会出现相同的错误?卸载包裹时,很可能任务没有完成。@uwerabe,这也是我的想法。在卸载包之前,必须对线程是否已完成进行某种确认。您在模块代码完成之前卸载了模块,否?我建议导出另一个检查此条件的函数
CanUnload
。最好从主程序检查,而不是在包内阻塞。这与COM的功能没有什么不同。当COM想要卸载DLL时,它首先请求DLL。如果DLL有任何COM对象处于活动状态,它会拒绝卸载,直到它们首先被释放。在调用P和UnloadPackage之间进行一些等待时,是否会出现相同的错误?卸载包裹时,很可能任务没有完成。@uwerabe,这也是我的想法。在卸载包之前,必须对线程是否已完成进行某种确认。您在模块代码完成之前卸载了模块,否?我建议导出另一个检查此条件的函数
CanUnload
。最好从主程序检查,而不是在包内阻塞。这与COM的功能没有什么不同。当COM想要卸载DLL时,它首先请求DLL。如果DLL有任何COM对象处于活动状态,它将拒绝卸载,直到首先释放它们。