Re: The curse of the Synchronize II.


2004-11-11 05:51:42 PM
delphi27
In article <XXXX@XXXXX.COM>,
XXXX@XXXXX.COM says...
Quote
After a mini-rant in one of the other groups, I thought that I would ask a
question here. I am not sure it is the right group, but anyway...
Hey there, Martin :)
Quote
For years, TThread.synchronize has been deadlocking and holding up apps,
aided and abetted by TThread.waitFor and A.P loops.
TThread.WaitFor, I can believe. I would warn people against using it - it was
just poorly designed (it won't drop through if the thread has already
ended). I haven't, though, honestly run into a case of
TThread.Synchronize that wasn't caused by something else sitting waiting
in the main loop.
Quote
I suggest that all TwinControl descendants woulld be better off with an
'onReceivedObject' event and 'postObject' methods. Similarly, TThread could
have an 'objectExecute(AObject:Tobject)' and 'postObject' methods. This
would allow developers to move objects around between threads without having
to resort to hard-sync or having to twiddle directly with API calls. Such
methods are easy to implement in Delphi with postMessage, Kylix with
QEvents, and doubtless can be implemented in any multitasking OS.
Given that most events happen from the main loop, I don't think that
would be justified for the workaday developer.
That said, I understand some of the pain. I set myself up a Notification
system early in my foundation project with Publish/Subscribe and
multithreaded environments in mind.
The key addition which made it work for different scenarios was to have
multiple dispatchers. You can have a threaded dispatcher (a new thread
runs and calls the event handler), a pooled thread dispatcher (a thread
on the pool calls the event handler), a synchronous dispatcher (the event
handler is called in-line; the sender waits, as per 'normal'), a last-
update dispatcher (only the most recent notification is sent) and a
synchronized dispatcher (runs from Synchronize()). This lets you handle
inter-thread notification with no impact on the main thread, but lets you
add notification that syncs up with the main thread for GUI objects.
(e.g. Target.AddChangeListener(Self,DISPATCH_SYNCHRONIZED) or
Target.AddChangeListener(TMethodListener.Create(Self,ListenMethod),
DISPATCH_SYNCHRONIZED))
Quote
Could something like this be added to D8/ .NET?
This addition, though, would not make things any easier for those who just
want to send a string. Declaring an object to hold a string, creating one,
loading it up, posting it & then freeing it at the other end is much more
complex than using the dreaded 'synchronize', and relies on the developer
explicitly freeing the object after using the string. Developers are not
'used' to explicitly freeing strings. Would it be better/safer to add an
'onReceivedString' and 'postString' methods that automagically queue copies
of the posted string in a 'safe' manner, so that they are deallocated at the
receiving end?
Quite frankly, I have been using interface-wrapped notifications for that
very purpose with no troubles. The wrapping gets a bit manual, but I
don't have an infinite number of kinds of notification :)
Quote
What do all you other designers/developers think? What can be done to ease
the development of multithreaded apps? How can we dump synchronize/waitFor
& still allow, (reasonably), simple thread implementations?
I've got a few thoughts on the subject, having been steeped in it for a
while. Personally, I would love to have the de{*word*81} more thread-aware, e.g.
have F8 go to the next instruction *on that same thread* (or have I
missed a new Delphi version for that? :).
I've changed the way I do threads, as well. Instead of having TThread
derivatives, I create objects that implement ICOREExecutable (with a sole
.Execute method) which various thread-implementing objects can run. That
helps focus on the classes instead of having to look at every concurrent
thing you need to do as "if I let this method finish, I can not use this
object ever again" (as with TThread.Execute).
I keep a separate Synchronize() method (I built my own, but due to
Borland hiding the bits I want, I may just make a single TThread in
charge of queuing up and running Synchronize requests :), so that
*anything* can do a Synchronize (you may have run into the trouble
yourself that the things you CALL can not do a Synchronize).
I also made a "Guard" interface that usually represents a critical, but
can be swapped out for a 'deadlock detector' that uses a
TryEnterCriticalSection/Sleep loop.
Last month's edition of ACM's Queue magazine had an article in it about
Debugging Concurrent Programs, but I am only halfway through the mag today
:)
Quote
Rgds,
Martin
Likewise :)
-- Ritchie Annand
Senior Software Architect
Malibu Software & Engineering Ltd.
Business: www.malibugroup.com
Personal: nimble.nimblebrain.net
Wiki: wiki.nimblebrain.net