Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi Queue and Synchronize

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:

  • Synchronize on the first and Synchronize on the second it works
  • Queue on the first and Synchronize on the second it works
  • Queue both times it's not working because I see only the start in the memo
like image 359
Raffaele Rossi Avatar asked Feb 16 '17 17:02

Raffaele Rossi


1 Answers

The 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.

  • Like mentioned in the comments you can call 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.
  • But... If you still need the thread active when executing the call (for some data from it), you'll need to block the thread from exiting. You can do that by using 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/

like image 122
Rik Avatar answered Nov 09 '22 22:11

Rik