13

If Delphi code was written with synchronize to serialize access to the main VCL thread, but this code then is used in a non-VCL application, will it synchronize with the main thread of the application or just have no effect at all?


Example:

procedure TMyThread.Execute;
begin

  // ... other code

  Synchronize(SomeMethod);

  // ...

end;

Let's assume

  • it is a non-VCL application which has a main thread which executes in an endless loop (or until terminated)
  • the main thread does not call CheckSynchronize directly or in a WakeMainThread handler
  • a secondary thread runs and executes Synchronize(SomeMethod) like in the example above

Will the thread hang on the Synchronize(SomeMethod) line?

mjn
  • 36,362
  • 28
  • 176
  • 378
  • Is the non-VCL application in your control? If not then you can't use `Synchronize`. – David Heffernan May 21 '12 at 16:48
  • @DavidHeffernan I can imagine a `synchronize(CallEventHandler)` in a library where I can assign a method to the event handler. The compiler will not complain if it is a non-VCL application. – mjn May 21 '12 at 16:58
  • Of course the compiler won't complain. It has nothing to do with the syntax of the command or the scope of the identifiers. But @David is wrong that you can't use `Synchronize`. You can use it all you want. You just have to publicize that you're using it so that the consumer of your library knows to use `CheckSynchronize`. I don't know what happens if the host doesn't honor its part of the contract, though. Feel free to write a test program and report your own results here. – Rob Kennedy May 21 '12 at 17:30
  • @Rob If the app is not in your control then how is it going to call CheckSynchronize? I think we are in accord here. – David Heffernan May 21 '12 at 17:36
  • Easy, @David. You put a note in the readme or the usage synopsis: "This library uses feature X. Therefore, consumers of this library need to do Y; VCL programs do it automatically." And in the FAQ: "Q: Why does the library hang when `EventHandler` is assigned? A: You have a non-VCL program and you have neglected to do Y." – Rob Kennedy May 21 '12 at 17:43
  • I don't know what you mean by "assign a WakeMainThread method to set CheckSynchronize." CheckSynchronize is not something you can "set," and it has nothing to do with whether WakeMainThread is assigned. – Rob Kennedy May 21 '12 at 17:47
  • 1
    @Rob If the code is a plugin, for example, then that would not be viable. – David Heffernan May 21 '12 at 17:48
  • `WakeMainThread` isn't required. The main thread could simply call `CheckSynchronize` of its own accord, without being told to. – Rob Kennedy May 21 '12 at 17:52

3 Answers3

9

TThread provides facilities for non-VCL programs to check the synchronization queue, so they can continue to use multithreaded libraries that expect to synchronize their methods. This is described in the documentation for CheckSynchronize. Remember that it's the application's job to check the queue, not your library's.

As long as the application honors its part of the contract, your use of Synchronize should be fine. If it doesn't, though, then your program will not work correctly, but I don't know what exact symptoms to expect. Hanging certainly sounds plausible.

Rob Kennedy
  • 161,384
  • 21
  • 275
  • 467
  • see my edit: the DocWiki is a bit unclear, it reads as if assigning WakeMainThread and calling CheckSynchronize is an optional optimization. – mjn May 21 '12 at 17:14
  • Assigning `WakeMainThread` is the optimization. It's expected that the main thread would wake itself *eventually* and notice the new synchronized tasks to perform, but `WakeMainThread`, if provided, tells the main thread to wake up *immediately*. It cuts down on latency. – Rob Kennedy May 21 '12 at 17:24
  • Sorry for deleting my previous comment! I have read in your comment `I don't know what happens if the host doesn't honor its part of the contract, though. ` and then deleted it assuming it is not so easy to answer – mjn May 21 '12 at 17:49
  • That detail isn't easy *for me* to answer because I don't have Delphi installed anywhere to test it. You do. – Rob Kennedy May 21 '12 at 17:54
  • 1
    The background thread blocks until CheckSynchronize has been called. What else could happen? – David Heffernan May 21 '12 at 17:56
6

Is it dangerous to use synchronize in a non-VCL application?

Yes it is dangerous. If your main thread is not calling CheckSynchronize then Synchronize will result in deadlock.

Let's assume

  • it is a non-VCL application which has a main thread which executes in an endless loop (or until terminated)
  • the main thread does not call CheckSynchronize directly or in a WakeMainThread handler
  • a secondary thread runs and executes Synchronize(SomeMethod) like in the example above

Will the thread hang on the Synchronize(SomeMethod) line?

The call to Synchronize will block the background thread until the main thread calls CheckSynchronize. So, if the main thread never calls CheckSynchronize, the background thread will block indefinitely.

The following program illustrates this:

program TheBigSleep;

{$APPTYPE CONSOLE}

uses
  SysUtils, Classes, Windows;

type
  TMyThread = class(TThread)
  protected
    procedure Execute; override;
  end;

procedure TMyThread.Execute;
begin
  Synchronize(SysUtils.Beep);
end;

begin
  with TMyThread.Create do
    WaitForSingleObject(Handle, INFINITE);
    //don't call WaitFor since that, in turn, calls CheckSynchronize
end.
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • 3
    +1, and about `Synchronize`, don't ever use it. The thread should not be dependant of what is outside of it's scope. Better to use non-blocking calls like `PostMessage` or `TThread.Queue`. See [mghie's](http://stackoverflow.com/users/30568/mghie) answer to [Is it better to use TThread's “Synchronize” or use Window Messages for IPC between main and child thread?](http://stackoverflow.com/a/1806947/576719) for a summary. – LU RD May 21 '12 at 18:36
  • `TThread.Queue()` uses the same queue as `TThread.Synchronize()`, and so has the same requirement that the main thread needs to call `CheckSynchronize()` periodically to process the requests from either function. – Remy Lebeau May 16 '22 at 23:33
1

Since you are putting the Delphi code in a library for other apps to use, I would not advise using Synchronize() at all, since your thread has no concept of what is going on outside of itself. A better choice is to have the thread expose a callback event that the thread can call in its own context when needed, and then let the app provide a callback function handler that decides the best way to synchronize with the app's main thread as needed.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770