In article <
XXXX@XXXXX.COM>, bryan@%nospam%
ivisionsystems.com says...
Quote
You don't want to be iterating lists in a multithreaded environment, as you
have to do:
ListLock;
IterateList;
ListUnlock;
Ow, ow, ow :)
I've been compiling a list of don't-dos in concurrent programming, and
making external calls of potentially unknown length is right near the
top :)
Quote
Now - if that list is long, or the operation you're doing takes more than a
few microseconds, this method will block the other threads from this list
for a long time. This may mean the the user interface is unresponsive and
laggy, or the animation jitters, or network connections timeout.
I created a free-standing Synchronize() routine that doesn't have to be
called from within a TThread to carry that as far as possible. It's
really handy being able to do a little lengthy calculation in something
the TThread *calls* to update the screen.
procedure TProcessForm.Receive(ANotification: INotification);
var
TaskNotification : ITaskDoneNotification;
begin
if Supports(ANotification,ITaskDoneNotification,TaskNotification) then
if TaskNotification.Status=tdsDone then
begin
Resolver.Resolve(ImporterTask.OutputSource);
Synchronize(CreateObjectBrowser);
end;
end;
The notification is threaded, so this stays in a thread until the
Synchronize call itself.
Quote
With the iterator, you only lock the list while getting the next element.
If you've fallen off the end of the list, you stop iterating.
repeat
ListLock;
NextElement := GetNextElement;
ListUnlock;
If NextElement <>nil then
NextElement.DoSomething;
until NextElement = nil;
I use this sort of construct all the time :)
Local variables are pretty safe, but then there's the snag...
Quote
See the advantage? You're only blocking other threads while you get the
next element. You still have the problem of another thread freeing your
object while you're working with it though. I spent 2 years with a
colleague designing an architecture to get round this problem, but it could
never fully work.
The solution we found was to reference EVERYTHING you
want to access from multiple threads as an interface. Reference counting
does the rest for you.
That's probably one of the greatest attractions interfaces had for me
when I started to get into them. Because the _AddRef and _Release use
interlocked functions, they're quick, threadsafe and they solved *so
many* of my race condition and thread contention (making the locks
longer to solve the race conditions) problems.
.NET turns a few things on their ears, most notably in the realm of
destruction. Objects -and- interfaces are thread-safe in it, but the
destruction semantics are definitely NOT thread-safe and high
performance at the same time.
Quote
Bryan
Nifty stuff, Bryan :)
-- Ritchie Annand
Senior Software Architect
www.malibugroup.com
nimble.nimblebrain.net
wiki.nimblebrain.net