Home > Net >  How to wait that all anonymous thread are terminated before closing the app?
How to wait that all anonymous thread are terminated before closing the app?

Time:05-12

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 enter image description here

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;
  • Related