4

I am using CreateAnonymousThread for a worker task, and when I started with it I used Synchronize within the entire declaration as per documented examples, e.g:

procedure Txxx.RunWorker;
begin
     FExecutionThread := TThread.CreateAnonymousThread(procedure ()
     begin

        TThread.Synchronize (TThread.CurrentThread,
          procedure ()
          begin
            // Here before worker stuff
            NotifyBeforeWorkerStuff; 

          end);

        // Do worker stuff

        TThread.Synchronize (TThread.CurrentThread,
          procedure ()
          begin
            // Here after worker stuff
            NotifyAfterWorkerStuff;
          end);
      end);

      FExecutionThread.Start;
    end;
end;

As you see, from within this thread I launch event notifications to various parts of my app including VCL forms (NotifyBeforeWorkerStuff etc). Later, I saw that I could move Synchronize() more locally to each VCL form close to the point that actually required it for updating (non-safe) VCL controls:

    procedure TSomeVCLForm.ReceiveNotification;
    begin
      TThread.Synchronize (TThread.CurrentThread,
          procedure ()
          begin
            Label1.Caption := GetSomeStringFunction;
          end);
     end;

The worker thread then becomes simpler as long as I live with notifications being from either main or worker threads:

   procedure Txxx.RunWorker;
   begin
     FExecutionThread := TThread.CreateAnonymousThread(procedure ()
       begin

         NotifyBeforeWorkerStuff; 

         // Do worker stuff

         NotifyAfterWorkerStuff;

       end);

     FExecutionThread.Start;
   end;

I have several questions about whether this is correct:

  1. My notifications may be from the worker thread but also from the main thread (e.g derived from a button press). So, when 'ReceiveNotification' on a VCL form is called from the main thread, is it allowed to call TThread.Synchronize as above? The the XE8 docs imply not, but checking System.Classes this looks ok and it works fine.
  2. Within 'ReceiveNotification' when Label1.Caption fetches the string from GetSomeStringFunction, is it correct that there is absolutely no need for locking within that function even when the call is from a worker thread?

Thanks for any advice.

Brian Frost
  • 13,334
  • 11
  • 80
  • 154

1 Answers1

6

The documentation says:

Warning: Do not call Synchronize from within the main thread. This can cause an infinite loop.

I think that documentation is simply wrong. The implementation in XE8 checks whether or not the current thread is the main thread. If it is then the method is executed directly.

No locking is required in ReceiveNotification because the call to GetSomeStringFunction is always performed on the main thread.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Thanks David, as I hoped! – Brian Frost Apr 28 '15 at 12:29
  • 1
    FYI, you do not need to use `TThread.CurrentThread`, you can specify `nil` instead. – Remy Lebeau Apr 28 '15 at 16:54
  • 1
    @BrianFrost: Yes. I imagine the parameter is there only to help the compiler differentiate between the `class` and non-`class` versions. If the parameter is not `nil`, the `class` version simply calls the non-`class` version on that `TThread` object, otherwise it just goes to the same queue directly. So it is always safe to specify `nil`, it avoids an unnecessary `CALL` instruction. – Remy Lebeau Apr 29 '15 at 17:18
  • @RemyLebeau, in a comment [Passing value to synchronize thread](http://stackoverflow.com/q/16870387/576719), you say that a call to `Synchronize` from the main thread to a procedure that also calls `Synchronize` to itself, will result in an infinite recursive loop. This would mean that the documentation is correct, but perhaps not informative enough. – LU RD Feb 16 '17 at 12:44
  • @LURD any method that calls itself can lead to an endless recursion loop. That has nothing to do with `Synchronize()`. – Remy Lebeau Feb 16 '17 at 15:50
  • @RemyLebeau I don't get it. This would only happen if the recursion did not terminate. There's no reason to believe that is the case. Method A calls Synchronize passing method B. Method B call Synchronize passing method C. Method C does not call Synchronize. And there it ends. – David Heffernan Feb 16 '17 at 15:54