I encounter an awkward problem. In my app I often do
TThread.createAnonymousThread(
procedure
....
end).start
The problem I have is that when I close the main form of my app, then sometime some of those AnonymousThread are still alive after the Tform.destroy finished . Is their a way in my Tform.destroy to wait that all those AnonymousThread (created a little everywhere in the whole app) are successfully terminated before to continue ?
I found this way to list all running thread (from
CodePudding user response:
Reading all threads from the process and then trying to figure out which ones to wait for sounds like the path to a lot of pain.
If you really don't want to store the references of the anonymous threads (which then by the way should not be FreeOnTerminate as that would cause dangling references if the thread ends before you wait for it) then build a wrapper around TThread.CreateAnonymousThread
or TTask.Run
which does the storage internally and encapsulates the WaitFor
. You could even be fancy and add an additional group key so you can create and wait for different set of threads/tasks instead of all.
CodePudding user response:
Push in a thread-safe list the TThread
references returned by CreateAnonymousThread
, make sure to keep it in sync when a thread terminates and implement your own WaitForAll
.
Or, consider to use TTask
class for a simple parallel threads management. It have already implemented WaitForAll
method.
Sample code took from Delphi help:
procedure TForm1.MyButtonClick(Sender: TObject);
var
tasks: array of ITask;
value: Integer;
begin
Setlength (tasks ,2);
value := 0;
tasks[0] := TTask.Create (procedure ()
begin
sleep(3000);
TInterlocked.Add (value, 3000);
end);
tasks[0].Start;
tasks[1] := TTask.Create (procedure ()
begin
sleep (5000);
TInterlocked.Add (value, 5000);
end);
tasks[1].Start;
TTask.WaitForAll(tasks);
ShowMessage ('All done: ' value.ToString);
end;
CodePudding user response:
The difficulty with answering questions like this, is they are programmatic techniques questions. I can offer you only my personal solution here from code that is in daily (Android) use. Other (more-experienced) programmers may create far better solutions, but they're not here attempting to answer this question! I hope you find this useful.
public
FThreadList: TObjectList<TThread>; // list of sending threads
procedure TfrmMain.FormCreate(Sender: TObject);
begin
FThreadList := TObjectList<TThread>.Create(True);
procedure TfrmMain.FormClose(Sender: TObject);
begin
WaitForAllThreads;
FThreadList.Free
procedure TfrmMain.WaitForAllThreads;
var
i, j: Integer;
AThread: TThread;
begin
LogMe('Found ' IntToStr(FThreadList.Count) ' sending threads');
i := 0; j := 0;
while i < FThreadList.Count do
begin
// Each thread is freeonterminate := False
// FThreadList has ownsobjects = True
AThread := FThreadList.Items[i];
if not AThread.Finished then
begin
Inc(j);
AThread.WaitFor;
end;
Inc(i);
end;
if j > 0 then
LogMe('Waited for ' IntToStr(j) ' sending threads')
else
Logme('Waited for none');
while FThreadList.Count > 0 do
begin
i := FThreadList.Count - 1;
FThreadList.Delete(i);
FThreadList.TrimExcess; // set capacity to count, freeing memory, yes
end;
end;
procedure TfrmMain.Sending(...);
var
AThread: TThread;
begin
if Self.isClosing then EXIT; // add no more threads
AThread := TThread.CreateAnonymousThread(procedure
begin
// this procedure connects to a REST server to do some work
end
);
AThread.FreeOnTerminate := False;
FThreadList.Add(AThread);
LogMe('FThreadList.count=' IntToStr(FThreadList.Count));
AThread.Start;