Delphi 在动态包库中使用TTask时内存泄漏
在动态包中使用TTask将导致调试器中的内存泄漏和访问冲突。在某些情况下,TTask似乎不适合在动态打包应用程序中使用 我在delphi包中定义了一个外部过程: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
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对象处于活动状态,它将拒绝卸载,直到首先释放它们。