15

Actually i am using this code and works ok, but i 'am wondering if is the correct way.

  while WaitForSingleObject(MyThread.Handle, 0) = WAIT_TIMEOUT do
    Application.ProcessMessages;

  ShowMessage('i am done');
Wim ten Brink
  • 25,901
  • 20
  • 83
  • 149
Salvador
  • 16,132
  • 33
  • 143
  • 245

5 Answers5

14

The VCL TThread class has its own WaitFor() method that pumps the main message queue internally when called within the main thread context:

MyThread.WaitFor; 
ShowMessage('i am done'); 
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
8

Calling Application.ProcessMessages is generally considered a code smell. Let your main thread idle if it's got nothing to do.

If you ran a company and needed one of your workers to run to the store and grab some much-needed supplies, would you then pace by the door until he got back, or would you prefer to sit in your office and rest and wait for him, and find out that the supplies are here because you hear him walk through the door? Either way, he'll take the same amount of time, but the first way's gonna wear your legs out.

Similarly, instead of having your UI watch the thread, have the thread report back to the UI. One way to do this is to have the thread use PostMessage to send a custom message to the form that launched it once it's finished, and put a message handler on the form to respond to it.

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
Mason Wheeler
  • 82,511
  • 50
  • 270
  • 477
  • Mason: You may want to say Application.ProcessMessages and not Application.PostMessages. – jachguate Nov 16 '10 at 03:50
  • There are some cases where all you have to do is to wait... for example to let the application terminate. If you use WaitFor on a (already known) lazy-to-terminate thread... a user (and windows) may think it is not responsive... – jachguate Nov 16 '10 at 03:51
  • Mason, many thanks for you response , can you provide a simple example (source code) of your answer? – Salvador Nov 16 '10 at 04:37
  • @Salvador: I probably could, but the documentation already does a good job of making it clear how it works. Check out http://docwiki.embarcadero.com/RADStudio/en/Declaring_a_New_Message-handling_Method and the related pages for an overview of the system, with example code. – Mason Wheeler Nov 16 '10 at 04:56
2

It looks correct (if correct means it do the work). What I would change is to wait for a bit more time (50ms looks good to maintain the application responsive) while not eating CPU.

while WaitForSingleObject(MyThread.Handle, 50) = WAIT_TIMEOUT do
  Application.ProcessMessages;
ShowMessage('i am done');

Sure there are other ways to do it... <joke>but I usually apply one of the main engineering principles:

if it works, don't touch it!</joke>

jachguate
  • 16,976
  • 3
  • 57
  • 98
  • It is not a good idea to call Application.ProcessMessages() when there are no messages to process. You should use MsgWaitForMultipleObjects() instead to detect when the message queue has something to process. – Remy Lebeau Nov 18 '10 at 02:53
  • @Remy: Thanks for that info... I'll usually avoid calling the ProcessMessages method, but sometimes I did it, mainly because I had no idea of a better approach. I'll check on MsgWaitForMultipleObjects to learn and improve that. Thanks again!. :) – jachguate Nov 18 '10 at 21:16
2

I agree with Mason Wheeler's remark, the main thread is best left to do its job, but I would suggest using the OnTerminate event on the thread. It is more 'Delphi natural' and the internal logic does the PostMessage bit for you. Since TThread is not a component, you can't view it in the object inspector and have to write and attach an event handler yourself. It gets called (in the main thread!) after the thread has completed/terminated.

Stijn Sanders
  • 35,982
  • 11
  • 45
  • 67
1

While it looks okay, like jachguate I would use a bigger time-out value than 0 too. If you use WaitForSingleObject(MyThread.Handle, 100) then the main thread will wait a bit longer, thus eating up less CPU cycles.
A better solution would be the use of messages, though. Your application starts the thread and then puts all controls in disabled mode. The thread then executes and when it's finished, use SendMessage or PostMessage to the main window to notify it that the thread is done again. Then your application will just enable every control (and whatever else) again. This has as advantage that you keep the "natural" messageloop for the application alive, instead of running your own messageloop with this solution.
Unfortunately, the message-method has one drawback: if the thread crashes then no message will be sent back, so a backup plan would be practical. For example, by adding a timer control to your mainform which checks every second if the thread is still alive. If not, it too would just activate the form again, disabling itself again.

Community
  • 1
  • 1
Wim ten Brink
  • 25,901
  • 20
  • 83
  • 149