Stefano Tommesani

  • Increase font size
  • Default font size
  • Decrease font size
Home

Easy multi-thread programming with Delphi

In the AltaPixShare software app, when the user drags a group of images to the target location, the following code iterates over the list of images, and for each image creates the destination file:

for i := 0 to Pred(ActiveImages.Count) do
begin
try
CurrentImage := TProcessedImage(ActiveImages[i]);
except
CurrentImage := nil;
end;
if Assigned(CurrentImage)
then begin
CurrentImage.CreateDestFile();
end;
end;

Given that applying a filter to a large image and then writing the output image into a JPEG file is a time-consuming task, this is a clear case for multi-threaded optimization, so that multiple images are processed at the same time on different CPU cores. And using the amazing AsyncCalls library, we can achieve this result with only a few lines of code. These are the steps required to turn this serial code into a multi-threaded one:

  1. add the AsyncCalls unit to uses list 
  2. add a variable-length array that contains the handles to the various async calls:
    ThreadCallsArray : array of IAsyncCall;
  3. set the size of the variable-length array to match the number of images to be processed in parallel:
    SetLength(ThreadCallsArray, ActiveImages.Count);
  4. instead of directly calling the method that builds the output file for each image in the list, create an async call for each image:
    ThreadCallsArray[i] := AsyncCall(@CreateDestFileOfImage, CurrentImage);
  5. finally, wait for all async calls to complete, and proceed with other computations:
    WaitResult := AsyncMultiSync(ThreadCallsArray, True, INFINITE);

The only troublesome spot in the code above is that the AsyncCall does not let you use the normal method invocation (CurrentImage.CreateDestFile()) but requires the usage of a custom-defined procedure that receives the object as a parameter and invokes the CreateDestFile on the given object:

procedure CreateDestFileOfImage(CurrentImage : TProcessedImage);
begin
if Assigned(CurrentImage)
then CurrentImage.CreateDestFile;
end;

So here is the code from AltaPixShare that does the parallel processing of images:

var
i: integer;
Res: TDragResult;
CurrentImage : TProcessedImage;
ThreadCallsArray : array of IAsyncCall;
WaitResult : Integer;
begin
if (ActiveImages.Count = 1)
then begin
/// one image only
try
CurrentImage := TProcessedImage(ActiveImages[0]);
except
CurrentImage := nil;
end;
if Assigned(CurrentImage)
then CurrentImage.CreateDestFile;
end
else begin
/// multiple images -> multi-threaded processing
SetLength(ThreadCallsArray, ActiveImages.Count);
for i := 0 to Pred(ActiveImages.Count) do
begin
try
CurrentImage := TProcessedImage(ActiveImages[i]);
except
CurrentImage := nil;
end;
if Assigned(CurrentImage)
then begin
ThreadCallsArray[i] := AsyncCall(@CreateDestFileOfImage, CurrentImage);
end;
end;
WaitResult := AsyncMultiSync(ThreadCallsArray, True, INFINITE);
end;

Please note that if there is only one image in the list, it is better to just process that image in the main VCL thread, as spanning a different thread and then waiting on that thread's completion would be a waste of resources.

Remember to store the interface returned by AsyncCall, as dropping it, setting it to NULL, or overwriting it with another call to AsyncCall (for example, in a loop that creates multiple threads) would force the thread to be executed in the main VCL thread, canceling the benefits we are trying to achieve by using multiple worker threads.

Before switching to a multi-threaded solution, be aware that you should check if the code is really thread-safe (if the code inside a thread tries to access the VCL, it's a recipe for troubles, as only the main VCL thread can work with it) and also check if third-party libraries are thread-safe of not: e.g. the JPEG compression library used by AltaPixShare is not thread-safe, so you get corrupted output images if you try to compress multiple JPEG files at the same time, so the JPEG function is called within a critical section that serializes access to that resource:

JPEGLibAccess.Enter;
try
SaveTo24bitJPEGFile(OutputBitmap, DestFilename, 90, false);
finally
JPEGLibAccess.Leave;
end;

A new article showing how to parallelize loops in Delphi has been published at this address.

Quote this article on your site

To create link towards this article on your website,
copy and paste the text below in your page.




Preview :


Powered by QuoteThis © 2008
Last Updated on Saturday, 20 April 2013 17:19  
View Stefano Tommesani's profile on LinkedIn

Latest Articles

Fixing Git pull errors in SourceTree 10 April 2017, 01.44 Software
Fixing Git pull errors in SourceTree
If you encounter the following error when pulling a repository in SourceTree: VirtualAlloc pointer is null, Win32 error 487 it is due to to the Cygwin system failing to allocate a 5 MB large chunk of memory for its heap at
Castle on the hill of crappy audio quality 19 March 2017, 01.53 Audio
Castle on the hill of crappy audio quality
As the yearly dynamic range day is close (March 31st), let's have a look at one of the biggest audio massacres of the year, Ed Sheeran's "Castle on the hill". First time I heard the song, I thought my headphones just got
Necessary evil: testing private methods 29 January 2017, 21.41 Testing
Necessary evil: testing private methods
Some might say that testing private methods should be avoided because it means not testing the contract, that is the interface implemented by the class, but the internal implementation of the class itself. Still, not all
I am right and you are wrong 28 December 2016, 14.23 Web
I am right and you are wrong
Have you ever convinced anyone that disagreed with you about a deeply held belief? Better yet, have you changed your mind lately on an important topic after discussing with someone else that did not share your point of
How Commercial Insight changes R&D 06 November 2016, 01.21 Web
How Commercial Insight changes R&D
The CEB's Commercial Insight is based on three pillars: Be credible/relevant – Demonstrate an understanding of the customer’s world, substantiating claims with real-world evidence. Be frame-breaking – Disrupt the

Translate