Classic usage of threads
Everyone who delves even a little seriously into programming with Delphi eventually faces the need to use additional threads. The fact is that in the architecture of such frameworks as VCL and FMX, the main program thread is responsible for rendering the user interface. Consequently, during any lengthy operations, this thread is occupied and does not render the user interface. This leads to the effect of the program freezing for a while. However, if the task can be parallelized, by launching several parallel threads we can not only eliminate the “program freezing” effect but also complete the task faster.
Everyone who has worked with older versions of Delphi (for example, Delphi 7) knows that to run a task in an additional thread, you need to:
- Create a class that inherits from the TThread class;
- Override the Execute method in it, placing all the work that should be performed in the additional thread there;
- Foresee the return of the result of the work using thread synchronization methods;
- Create an object of this class;
- Initialize the necessary input data;
- Start the additional thread;
- Remember to monitor the lifecycle of this object, or set the FreeOnTerminate property to True.
Let’s be honest, the amount of additional code required with such an implementation sometimes exceeded the code of the actual work placed in the additional thread. Programmers preferred to use threads only when it was really necessary. I personally know programmers with over 15 years of experience who continued to do everything in the main thread without launching additional threads.
Modern usage of threads
But progress does not stand still. Delphi is evolving as well. In subsequent versions, with the introduction of anonymous procedures and the integration of the parallel programming library, Delphi provided the capability to offload a task to a separate thread using the TTask class. Let’s consider how this looks in practice:
This example is useless in terms of functionality as we simply load the page https://www.google.com/ in a separate thread and do nothing with it. However, it demonstrates how easy it is to offload work to a separate thread now. Let’s analyze the code a little bit, which will be helpful for those who are not yet familiar with this class.
First, we need to include the Threading unit, which declares the TTask class. Next, in the button click handler in the main thread, we should prepare the initializing data for our task (assigning a value to the xUrl variable). The next step is calling the class function TTask.Run. This function returns an interface of type ITask, through which we can interact with the task later on, check its status, cancel it, wait for it, etc. In our case, we don’t need this, so we won’t assign the result of the Run function to anything. We will pass an anonymous procedure as a parameter to this function (they are written directly in the code without a declaration, that’s why it is simple to use them conveniently in such situations). Note that we didn’t pass the xUrl variable. We just used it within the body of the anonymous procedure (the compiler ensured that we could have access to it within the procedure’s body). In the procedure itself, we simply created a TIdHTTP component, loaded the content, and destroyed the component.
Thus, the main thread only formed the string and launched the task for execution. During the page loading, the user interface remained fully responsive without any “freezing” effect. For this, we only needed to write an additional five lines of code (and this includes the lines with ‘begin’, ‘end’, and ‘);’).