Re: Why doesn't my thread terminate?
- From: Prodigal Son <prodigal.son@xxxxxxxxxxx>
- Date: Mon, 05 May 2008 21:37:03 GMT
Rob Kennedy <me3@xxxxxxxxxxx> wrote in
news:6826lcF2phiv3U1@xxxxxxxxxxxxxxxxxx:
<snip
-- Here I declare the thread, and start it
procedure TMainForm.Button9Click(Sender: TObject);
var
thread : TMessageThread;
begin
thread := TMessageThread.Create(true);
thread.FreeOnTerminate := True;
thread.OnTerminate := TMessageThreadTerminate;
thread.Execute;
No. Never call Execute.
What you're doing is simply calling the TMessageThread object's
Execute method in the context of the current thread. It's no different
from calling any other method on any other regular class. Remember
that TThread is just a class; there's nothing inherently magical about
it that causes its methods to run in different OS threads.
Don't know why I didn't spot this before - I come from a Java background,
where it's basically the same. Create the thread, override "run" but call
"start", obviously for the same reason.
It looks like you created the thread object suspended. A suspended
thread does not execute until resumed, so when you're ready for the
thread to run, call Resume on the object.
Yes, that did the trick. It also solved another problem I noticed before:
when I attempted to move the form, the "thread" would stop (because it
wasn't running as a separate thread, which I now realize).
-- After the thread has terminated, it should say so
procedure TMainForm.TMessageThreadTerminate(Sender: TObject);
begin
ShowMessage('Terminated');
end;
And it _will_ say so, if the OS thread is ever given a chance to
terminate. But you're not even letting it run. :)
Et voila, it does now!
-- Override for the execute procedure.
procedure TMessageThread.Execute;
var
i: Integer;
begin
ShowMessage('Start');
No. Never call a VCL method from within a non-VCL thread. ShowMessage
involves creating a TForm and calling all sorts of VCL methods that
mustn't ever be called from secondary threads. Only do VCL stuff in
the main VCL thread.
I even failed to notice that ShowMessage was a VCL method - luckily for me,
it wasn't needed in the final example. Another lesson learned.
If a secondary thread needs to execute VCL stuff, then use the
thread's Synchronize function.
.... which is what I've done now. Together with the advice Skybuck gave me
I've come to a nice clean design which works great! Each time something
needs to be done VCL-wise, I use an event which will of course be handled
by the Synchronize function.
for i := 0 to 1000 do
begin
Application.ProcessMessages;
No. There is only one Application object, and it receives all its
messages in the main VCL thread since that's where it was created.
(Message queues have thread affinity. If a thread has a message queue,
it is completely separate from the message queues of any other threads
unless you do some special setup, which you obviously haven't done,
and which you definitely shouldn't do.)
This was just a way of wasting time inside the thread, instead of posting
the entire code. But again, I failed to notice that this was wrong in the
context of a new thread. Thanks!
In retrospect, I should've used sleep, but then I'd fail to learn this
lesson ;)
Calling Application.ProcessMessages from a non-VCL thread is always
wrong. When you do it, the Application object ends up processing
messages from the current thread's queue instead of from the queue it
expects to be reading from. Then it calls methods on itself and other
objects that it expects are bound to the main thread, but now they're
executing in an entirely different thread.
The only reason this appeared to work in your test was that you called
the Execute method directly from your other code, so everything ended
up executing in the main VCL thread anyway.
Which in turn was precisely the reason my "thread" stopped working as soon
as I moved/resized the form...
end;
ShowMessage('Stop');
Terminate;
You have the VCL source code. Go take a look at what the Terminate
function does. The only thing that happens is that it sets the
thread's Terminated property to True. The only reason for that is so
your Execute method can check the Terminated property and know to stop
executing.
What really signals a thread's termination is when the Execute method
stops running. In your case, you achieve that simply by falling off
the end of the procedure. Once the Execute method returns to its
caller, other internal TThread code goes through the operations
necessary to clean up the underlying OS thread and to execute the
OnTerminate event handler.
Thanks to your explanation, I now understand what went wrong and I've been
able to fix all this - thank you very much Rob, for taking the time to
explain this to me!
P.S.
.
- References:
- Why doesn't my thread terminate?
- From: Prodigal Son
- Re: Why doesn't my thread terminate?
- From: Rob Kennedy
- Why doesn't my thread terminate?
- Prev by Date: Re: uRe: Why doesn't my thread terminate?
- Next by Date: Re: Delphis End ?
- Previous by thread: Re: Why doesn't my thread terminate?
- Next by thread: Strange Mixer effects.
- Index(es):