Jump to content
Jeff Steinkamp

Thread or ProcessMessage?

Recommended Posts

I am returning to Delphi and Object Pascal after a 15+ year hiatus over in Visual Studio with C# and VB.  I think the last IDE I used might have Delphi 2 or 3.  So, working my way around this 10.4 monster has been somewhat of a challenge.

 

I have built a File Download Dialog with a progress bar and some labels to show total byte count and a running total of bytes downloaded.   I'm using the Indy IDHttp component with the SSL handler to fetch the files and the associated events.

 

I am updating the UI with the Application.ProcessMessage pump, but I'm not sure this is the proper way to update a UI and maybe threading this is the proper solution.   If that is the case, then can someone point me in the right direction on how to properly use threads as my google search have turned up a lot of 'mis-information' to say the least.  This seems to work fine with a 25MB file, but I'm not sure how it will work with 350MB file!!

 

I can upload my code if someone is interested.

Share this post


Link to post

As a general rule of thumb, never call Application.ProcessMessages by yourself. Let your thread "ping" back with updates from time to time with TThread.Synchronize, you can even have a TTimer in your VCL thread to poll updates from the Thread via public properties. Just make sure you are using some king of locking (TCriticalSection, TMonitor, mutexes, events, etc.) when reading from and writing to these variables.

Share this post


Link to post
4 hours ago, Dalija Prasnikar said:

You can find simple demo how to use threads with Indy here: https://github.com/dalijap/code-delphi-thread-safety/tree/master/Part3/19 Indy

 

This demo is using anonymous thread, but you can also use regular threads or TTask from Parallel Programming library, too. 

 

If you can post your code I can give you more detailed advice.

I took a page out of this Indy demo and applied it to my code, and once I got the FileStream creating straightened out for the thread, it is functioning quite well indeed!.  I have attached the file for that unit if you want to have a look and see if there is anything that can be done differently.

FileDownload.dfm

FileDownload.pas

Share this post


Link to post
6 hours ago, Jeff Steinkamp said:

I took a page out of this Indy demo and applied it to my code, and once I got the FileStream creating straightened out for the thread, it is functioning quite well indeed!.  I have attached the file for that unit if you want to have a look and see if there is anything that can be done differently.

It just needs few tweaks.

 

First, you don't need Application.ProcessMessages anywhere - they have been commented out, but the whole point of threads is getting rid of those. So you can just forget about it 🙂

Next, this will work fine, but in case user closes the dialog before download completes already queued synchronization methods will access released form and crash. To prevent that, you need to add additional filed in Form declaration and OnCloseQuery event handler on the form. I also fixed try finally block inside thread as constructing HTTP instance can also fail (not likely event, but still), and in that case the cleanup was not proper one. I also added try..except block to show error message in case of failure. 

 

If the whole application is closed then anonymous threads can be killed at any time. If this is a possibility, then you might want to replace TThread.CreateAnyonymousThread with TTask.Run (without Start) from System.Threading as all running tasks will be waited for on application shutdown. 

 

 

 

FileDownload.pas

FileDownload.dfm

Share this post


Link to post
8 hours ago, Dalija Prasnikar said:

 

If the whole application is closed then anonymous threads can be killed at any time. If this is a possibility, then you might want to replace TThread.CreateAnyonymousThread with TTask.Run (without Start) from System.Threading as all running tasks will be waited for on application shutdown. 

 

 

Thanks for your updates!!  I was concerned about this, and I will look into getting rid of the Anonymous Thread and replacing it.  First, I will see if I can duplicate that behavior.  I think this will suffice for my purposes and I should be able to use this in other projects that I am considering.

Share this post


Link to post

hi @Jeff Steinkamp

 

As "CreateAnonimous" return a "Thread" you can do it:

  1. create a Array (global or a list protected to multi-access) to store (reference, id, etc...) all thread (anonimous or not) created
    1. if using "protected list" (using TMonitor, TCriticalSection, TSemaphore,  etc...) you can control it better, but I think that it's not necessary at all (see each situation in usage)
    2. if necessary, you can "remove" the thread reference in these list using "OnTerminate or other" when this thread is ended... if possible!
  2. when your form needs to be closed, you can scan this array and see if exists any thread on list and try terminate it, free it etc... this task can be done in "OnCloseQuery()", then, "OnClose()" is free to anothers tasks, you decide!
  3. I have done some like:

function TerminateMyTasksFirstToLast(AStartFromFirst: boolean = false): integer;

... scan all array with thread reference stored and try end it...

 

OnCloseQuery event:

  CanClose := (TerminateMyTasksFirstToLast() = 0); // if have any thread running... wait end it

 

Edited by programmerdelphi2k

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×