Steve F
Steve F

Reputation: 1567

How to make the main (calling) thread wait for a child thread to complete execution?

Using: Delphi 10 Seattle, Win32 VCL forms application

I'm developing an updater application that checks for updates to one or more installed software applications, and when updates are found will download the updates in sequence. After each update is downloaded, it will install the update before proceeding to download the next update. The downloading bit is implemented as a thread class (descendant of TThread) and its constructor is as follows:

constructor TWebFileDownloaderThread.Create(CreateSuspended: Boolean; const AWebFileURL, ALocalFilePath: String;
  ACallBackProc: TProgressCallback; AProxySetting: TProxySetting);
begin
  inherited Create(CreateSuspended);

  FWorkResult := False;

  FWebFileURL := AWebFileURL;
  FProxySetting := AProxySetting;
  FLocalFilePath := ALocalFilePath;

  FUpdateCallbackProc := ACallBackProc;
end;

The main thread creates and starts the downloader thread as follows:

procedure TfmMain.DownloadUpdateFromWeb(const AInstallerFileURL: String);
var
  internet_file_download_thread: TWebFileDownloaderThread;
begin
  internet_file_download_thread := TWebFileDownloaderThread.Create(True, AInstallerFileURL, FUpdateDownloadDir,
    UpdateProgressCallback, FProxySetting);

  internet_file_download_thread.OnTerminate := WebFileDownloaderThread_TerminatedMethod;
  internet_file_download_thread.FreeOnTerminate := True;
  internet_file_download_thread.Start;
end;

My specific question is: How to make the main (calling) UI thread wait until a downloader thread completes, before creating a new downloader thread to start the next download?

I believe that there is some form of queuing required, but not sure how to implement it. Your tips and advice are much appreciated.

Upvotes: 4

Views: 590

Answers (2)

Ondrej Kelle
Ondrej Kelle

Reputation: 37211

If I understood it correctly, you have a list of URLs to download and install (obtained by previously performing a check for updates). You want to download updates from these URLs and install them one by one: download update 1, install update 1, download update 2, install update 2, etc.

Here's a possible design:

In your WebFileDownloaderThread_TerminatedMethod, start installing the just downloaded update (to keep the main thread responsive, do this in a separate thread).

In the OnTerminate handler of the installer thread, remove the just completed URL (or mark it as processed) and start downloading the next one, by calling DownloadUpdateFromWeb again, unless the list is already empty (or contains no more unprocessed items).

(BTW, the method DownloadUpdateFromWeb would better be named something like BeginDownloadUpdateFromWeb, to indicate its asynchronous nature.)

Upvotes: 3

David Heffernan
David Heffernan

Reputation: 612804

You should not block the main thread. So do not wait until the worker thread completes. Instead arrange for the worker thread to signal to the main thread when it has finished. For instance by:

  • Sending a message, or
  • Using TThread.Synchronize, or
  • Using TThread.Queue, or
  • Handling the OnTerminate event of the thread, or
  • Some other form of inter-thread communication.

The OnTerminate looks like a pretty good option to me.

You might also consider using a higher level parallel library. For instance the RTL's parallel library, or OTL. That way you can avoid getting tangled up in the details of threads, and let the parallel library deal with such matters.

Were you to do this you could design your application with a producer/consumer architecture:

  1. Create a threadsafe queue.
  2. Create one or more download threads, these are the consumers.
  3. The main thread, the producer, pushes download jobs onto the queue.
  4. The consumer threads wait on the queue and when it contains jobs, the consumer threads pull off jobs and process them, by downloading the file.

This sort of design is very simple to implement using a high level parallel library.

Upvotes: 3

Related Questions