How to terminate anonymous threads in Delphi on application close?

anonymous, delphi, delphi-xe2, multithreading, terminate

Solution

If you want to maintain and exert control over a thread's lifetimes then it must have `FreeOnTerminate` set to `False`. Otherwise it is an error to refer to the thread after it has started executing. That's because once it starts executing, you've no ready way to know whether or not it has been freed.

The call to `CreateAnonymousThread` creates a thread with `FreeOnTerminate` set to `True`.

The thread is also marked as FreeOnTerminate, so you should not touch the returned instance after calling Start.

And so, but default, you are in no position to exert control over the thread's lifetime. However, you could set `FreeOnTerminate` to `False` immediately before calling `Start`. Like this:

MyThread := TThread.CreateAnonymousThread(MyProc);
MyThread.FreeOnTerminate := False;
MyThread.Start;

However, I'm not sure I would do that. The design of `CreateAnonymousThread` is that the thread is automatically freed upon termination. I think I personally would either follow the intended design, or derive my own `TThread` descendent.

Problem

I have a Delphi application which spawns 6 anonymous threads upon some TTimer.OnTimer event. If I close the application from the X button in titlebar Access Violation at address $C0000005 is raised and FastMM reports leaked TAnonymousThread objects. Which is the best way to free anonymous threads in Delphi created within OnTimer event with TThread.CreateAnonymousThread() method? SOLUTION which worked for me: Created a wrapper of the anonymous threads which terminates them upon being Free-ed. ``` 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; ``` End here is how you can call it: ``` 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; ``` No memory leaks, proper shutdown and easy to use.

Original source