Reputation: 1440
I got an impression that a thread which should be at least a little controllable from main thread must not use Synchronize()
at all: any attempt to do this almost immediately results in deadlock in one case or another.
As a consequence, onTerminate event should be avoided also as it's called using Synchronize.
For example, I have a thread which does some hard background work. I want to watch its progress in status bar and be able to stop it 'gracefully' if user pressed close button.
I can't use FreeOnTerminate := true
, because as thread was started I must not call any of its methods at all: at any moment it might be destroyed and I get access violation (or corrupt something completely).
So, thread destruction is main thread's responsibility. It would be logical to do all the 'finishing' job in some sort of DoTerminate procedure and point onTerminate event to it. But that's deadlock: we can't free the thread from DoTerminate because in TThread.Free there is WaitFor which can't complete before synchronized onTerminate event finishes.
Not just OnTerminate: if worker thread has any of Synchronize calls in it (for example, it wants to inform us that some percents of work was done etc), there is possibility of deadlock if at this very moment main thread is going to do smth with worker thread: they block each other!
So, in my understanding, the only way of using Synchronize is to delegate all the calls to worker thread side! For example, we make it with FreeOnTerminate := true
and it sometimes uses Synchronize() to tell us its progress, or that it's done already and going to be destroyed. And only inside these procedures we're able to control it, but it makes graceful shutdown 'at will' almost impossible or overcomplicated.
Am I missing something (some internal workings which help to overcome these deadlocks somehow)? Because I'm a little surprised: Synchronize() is one of first methods described in Delphi multi-threading manuals. Is it really so useless?
Upvotes: 3
Views: 284
Reputation: 31393
There are perfectly valid use cases for Synchronize
, but you have to be careful about identifying what those use cases are and ensure that you are writing the code such that you explicitly avoid the possibility of a deadlock situation. This is perfectly possible, but it requires careful design.
You should generally only use Synchronize
when you need the worker thread to wait for the main thread to complete the work before itself continuing. In most cases this is not necessary and it should generally be preferred to use TThread.Queue
instead. Using Queue
posts the work to the main thread but then immediately returns without waiting for the main thread to process the work. This avoids most of the deadlock traps that surround the use of Synchronize
.
The only danger with Queue
is overloading the main thread. If you have workers that are posting work to the main thread faster than the main thread can keep up then you can end up with a situation where your main thread locks up processing the queued work.
Upvotes: 9