I am reading Nick Hodges online and I have discovered the Queue but it is not behaving as I've expected and I couldn't understand what him and the documentation say. Look at this code:
TThread.CreateAnonymousThread(
procedure
begin
TThread.Queue(TThread.Current, procedure
begin
Memo1.Lines.Clear;
Memo1.Lines.Add('start');
end);
Sleep(2000);
TThread.Synchronize(TThread.Current, procedure
begin
Memo1.Lines.Add('end');
end);
end
).Start;
I always use Synchronize
but this time I have tried with Queue
because according to Nick it is better in case of multiple requests since they won't be "serialized" and executed one by one. The code above works fine. Why this is not working instead?
TThread.CreateAnonymousThread(
procedure
begin
TThread.Queue(TThread.Current, procedure
begin
Memo1.Lines.Clear;
Memo1.Lines.Add('start');
end);
Sleep(2000);
TThread.Queue(TThread.Current, procedure
begin
Memo1.Lines.Add('end');
end);
end
).Start;
In this case the Memo outputs the start
but not the end. When I call:
start
in the memoThe difference between queue and synchronize is that Synchronize()
puts the call in a queue and waits for that call to be completed and Queue()
puts the call in the queue and directly returns control to the thread.
However... and this is not mentioned in the official documentation, when a thread finishes, all the calls placed in the queue with Queue(AThread, AMethod)
, where AThread is its own thread, are removed.
You can see that clearly in the source of TThread.Destroy()
where RemoveQueuedEvents(Self)
is called.
RemoveQueuedEvents removes queued method calls. [...] If AThread is specified, then all method calls queued by this thread are removed.
So directly after your last Queue()
your thread ends, TThread.Destroy()
is executed and that last call(s) is/are removed from the queue.
There are some things you can do to solve this.
TThread.Queue(nil, AMethod)
. B.T.W. Calling TThread.Queue(AMethod)
is the same as TThread.Queue(Self, AMethod)
so you'll always need to use the nil-variant if the thread is going to end and you want the call to finish.Synchronize()
as last queue-method. Note that the last synchronize doesn't have to be a real procedure. You can just call synchronize to a dummy-procedure at the end of the TThread.Execute
like Synchronize(DummySync)
(example). The queue if FIFO so the thread will wait until all calls in the queue are processed (including the empty dummysync).Some extra information can be found on these pages
Ensure all TThread.Queue methods complete before thread self-destructs
http://www.uweraabe.de/Blog/2011/01/30/synchronize-and-queue-with-parameters/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With