我有一个Delphi应用程序,它在一些TTimer.OnTimer事件上产生6个匿名线程。
如果我从标题栏中的X按钮关闭应用程序,将引发地址$C0000005处的访问冲突,并且FastMM报告泄漏的TAnomousThread对象。
释放Delphi中使用TThread.CreateAnonymousThread()方法在OnTimer事件中创建的匿名线程的最佳方式是什么?
对我有效的解决方案:
创建了匿名线程的 Package 器,该 Package 器在被释放时终止它们。
type
TAnonumousThreadPool = class sealed(TObject)
strict private
FThreadList: TThreadList;
procedure TerminateRunningThreads;
procedure AnonumousThreadTerminate(Sender: TObject);
public
destructor Destroy; override; final;
procedure Start(const Procs: array of TProc);
end;
{ TAnonumousThreadPool }
procedure TAnonumousThreadPool.Start(const Procs: array of TProc);
var
T: TThread;
n: Integer;
begin
TerminateRunningThreads;
FThreadList := TThreadList.Create;
FThreadList.Duplicates := TDuplicates.dupError;
for n := Low(Procs) to High(Procs) do
begin
T := TThread.CreateAnonymousThread(Procs[n]);
TThread.NameThreadForDebugging(AnsiString('Test thread N:' + IntToStr(n) + ' TID:'), T.ThreadID);
T.OnTerminate := AnonumousThreadTerminate;
T.FreeOnTerminate := true;
FThreadList.LockList;
try
FThreadList.Add(T);
finally
FThreadList.UnlockList;
end;
T.Start;
end;
end;
procedure TAnonumousThreadPool.AnonumousThreadTerminate(Sender: TObject);
begin
FThreadList.LockList;
try
FThreadList.Remove((Sender as TThread));
finally
FThreadList.UnlockList;
end;
end;
procedure TAnonumousThreadPool.TerminateRunningThreads;
var
L: TList;
T: TThread;
begin
if not Assigned(FThreadList) then
Exit;
L := FThreadList.LockList;
try
while L.Count > 0 do
begin
T := TThread(L[0]);
T.OnTerminate := nil;
L.Remove(L[0]);
T.FreeOnTerminate := False;
T.Terminate;
T.Free;
end;
finally
FThreadList.UnlockList;
end;
FThreadList.Free;
end;
destructor TAnonumousThreadPool.Destroy;
begin
TerminateRunningThreads;
inherited;
end;
这里的结尾是你可以这样称呼它:
procedure TForm1.Button1Click(Sender: TObject);
begin
FAnonymousThreadPool.Start([ // array of procedures to execute
procedure{anonymous1}()
var
Http: THttpClient;
begin
Http := THttpClient.Create;
try
Http.CancelledCallback := function: Boolean
begin
Result := TThread.CurrentThread.CheckTerminated;
end;
Http.GetFile('http://mtgstudio.com/Screenshots/shot1.png', 'c:1.jpg');
finally
Http.Free;
end;
end,
procedure{anonymous2}()
var
Http: THttpClient;
begin
Http := THttpClient.Create;
try
Http.CancelledCallback := function: Boolean
begin
Result := TThread.CurrentThread.CheckTerminated;
end;
Http.GetFile('http://mtgstudio.com/Screenshots/shot2.png', 'c:2.jpg');
finally
Http.Free;
end;
end
]);
end;
没有内存泄漏,关机正常,使用方便。
3条答案
按热度按时间oyt4ldly1#
如果您想要维护和控制线程的生存期,那么它必须将
FreeOnTerminate
设置为False
。否则,在线程开始执行后引用它是错误的。这是因为一旦它开始执行,您没有现成的方法知道它是否已被释放。对
CreateAnonymousThread
的调用创建了一个将FreeOnTerminate
设置为True
的线程。该线程还被标记为FreeOnTerminate,因此在调用Start之后不应接触返回的示例。
因此,但默认情况下,您无法对线程的生命周期施加控制。但是,您可以在调用
Start
之前将FreeOnTerminate
设置为False
。就像这样:然而,我不确定我会不会那样做。
CreateAnonymousThread
的设计是线程在终止时自动释放。我个人认为,我要么遵循预期的设计,要么衍生出我自己的TThread
后代。nx7onnlm2#
要避免使用
CreateAnonymousThread
出错,只需在启动之前将FreeOnTerminate
设置为False
即可。这样,您就可以像往常一样使用线程,而无需任何变通方法。
您可以阅读说明
CreateAnonymousThread
自动将FreeOnTerminate
设置为True
的文档,这就是在引用线程时导致错误的原因。5m1hhzi43#
让您的线程关注来自外部的某种通知。这可能是发出信号的事件、发送到线程拥有的窗口的消息、通过您的线程侦听的套接字发送的命令或您找到的任何其他通信形式。
如果您确定这个问题是因为您的线程是所谓的“匿名”线程,那么一个简单的解决方法就是使它们成为非匿名线程。将匿名函数体放入
Execute
方法,并通过其构造函数将任何捕获的变量传递给线程类。