Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi TTask WaitForAll vs. Synchronise

I have only limted experience with threads. I want to read some fundamental database tables in parallel and I need to wait until all tables are read before the program can reasonably progress. In that regard, blocking the main thread is OK for me.

This code (simplified) works fine:

procedure ReadDBMultiThread;
  var ATasks : Array of ITask;
begin
  SetLength(ATasks, 3);
  ATasks[0] := TTaskCreate(procedure() begin DB_ReadTable1; end);
  ATasks[1] := TTaskCreate(procedure() begin DB_ReadTable2; end);
  ATasks[2] := TTaskCreate(procedure() begin DB_ReadTable3; end);

  ATasks[0].Start;
  ATasks[1].Start;
  ATasks[2].Start; 

  TTask.WaitForAll(ATasks);
end;

However, let's say I would like to update the main form to show the progress, i.e. which database table has already been read (or do any other necessary main thread work). Obviously, I cannot use Synchronise() because that would lead to a dead lock with WaitForall() and I cannot use Queue() because that would by executed after WaitForAll() finishes.

So, is there a good solution to solve this "WaitForAll vs Synchronise" situation? I guess, this must be a situation many people come into... the need to wait for all being finished, but wanting to update the main thread...

I thought about something like this, given in pseudo-code, replacing the WaitForAll() statement:

repeat 
  Applicaton.ProcessMessages; // or "ProcessSynchroniseMessages"
until "AllTaskCompleted"(ATasks);

Would this work? Is there a better solution?

Could I write a own routine like ProcessMessages, but restricted to Synchronise messages, i.e. other main form events not being executed until later?

Many thanks in advance!

like image 971
HJay Avatar asked Oct 12 '25 08:10

HJay


1 Answers

TThread.Synchronize() and TThread.Queue() do not use window messages (well, there is a message to "wake up" the main thread to signal a pending request, but the actual synchronizing itself is not message-based). Requests are put into a global queue, which the main thread checks when it is sitting idle, or when it detects the "wake up" message. You can pump that same queue manually by calling the Classes.CheckSynchronize() function directly:

while not TTask.WaitForAll(ATasks, 1000) do
begin
  // process any pending TThread.Synchronize() and TThread.Queue() requests
  CheckSynchronize(0);
  // process any pending UI paint requests, but not other messages
  Application.MainForm.Update;
  // anything else you need...
end;
like image 101
Remy Lebeau Avatar answered Oct 15 '25 09:10

Remy Lebeau