[fpc-pascal] RE: Threads executing in sequence instead of parallel

memsom memsom at interalpha.co.uk
Fri Sep 29 18:47:55 CEST 2006


Some rambling observations:

> I was fighting the serial execution - and then I ran that little tiny test
> program - and it worked. I tried to fix my code some more - same problem -
> but the little test program worked... so I finally (this is what I mean
> when
> I say I resigned to design changes) adopted that EXACT example as the
> foundation for how I would implement my THREADS.

I think your issue was possibly related to misuse of API. Certainly, on
the WIN32 platform, unless you use the WaitForXXXXX or Sleep functions,
you'll end up with serial processing because your task is too processor
intensive and will starve the other process of any time slices. The
scheduler can't context switch as easily if there is a contiguous task
taking up processor time, and if you're doing things that can't be
interrupted (e.g. copying large chanks of data from one file to another)
it can't always (ever?) interrupt the task safely.

As mentioned before, use Events, Sepahores and such to create a "state
machine" alike. This will then break the work up into chunks, hopefully.

Multithreaded programming is a completely different paragdim to standard
single threaded programming.


> I create my threads like a thread pool, I create them all suspended.
> (Because that parts works pretty easy)...

Okay, that will work.

> I set up the EXECUTE MY THREAD... as a loop... BUT - Each iteration calls
> its own SUSPEND method.... So it does a cycle and all I have to do is re
> initialize the "parameters" it works with BEFORE I resume it.... So its...
> as I call it "ready to fire" again, and then I just resume from the main
> program that is handling all these threads.

Eeek!! Suspending and resuming threads on Win32 is extremely unreliable
IMO. Far better to have an event (as in win32 API, not Delphi "Event"),
have the thread wait on it being signalled (e.g. WaitForSingleObject) and
then restart the thread at that point!!

Suspending a running thread is a great way to cause a Deadlock too.

> Basically the test program has a working example of two threads looping at
> the same time and the simple output shows they are truly multi-tasking...
> I
> built my app into this same idea - with the addition that I suspend the
> loop
> each go round.... the suspended threads in turn become my "ready to do
> something" ones. If they aren't idle - they are either busy or trying to
> find the meaning of life.

Creating threads suspended is a very good idea. This way you can start
them in a specific sequence. If you create them as the thread object is
created, there is a chance you'll create a race condition because of the
potential lag of the data initialization with in your TThread descendent.


> One of the biggest "issues" which is hard to debug if you aren't looking
> for
> it ... is WHY does a thread stay working when it should have finished the
> loop? It COULD be a bug in some dumb routine - but in many of my own cases
> -
>
> - it is accessing data that I wanted "SHARED" by all the threads... I know
> there is this critical section stuff and all that... but I found it
> simpler
> to make a RECORD STRUCTURE INSIDE the Threaded class that has all the DATA
> relevant to that INSTANCE - and then write "shared routines" that PASS
> this
> structure by value - so there is less need to worry about sharing data
> across the threads.

Stay away from Critical Sections. They are a complete hack IMO. They breed
logic races. You'll end up debugging weird deadlocks because one thread is
waiting to enter a piece of code in a critical section whilst another is
in the critical section waiting on the first to complete the task it is
waiting on the second to exit the critical section on. And that is just
with two threads ;-) Use PostThreadMessage for comms, and don't share non
essential data between threads. Create a multiread single write mechanism
for your stored shared data.


> Now - to handle the ABSOLUTELY MUST allow main app to talk with thread
> etc.

Use a locking mechanism... Create two methods:

function Lock(const timeout: cardinal): boolean;
procedure Unlock;

make Unlock only Unlock the locker if the lock is owned by the calling
process. Make the Lock attempt to lock for up to the timeout (miliseconds)
and if the lock fails handle that. You then can gain exclusive access tot
he thread.

All you then do is make the thread use the locker whenever it accessed the
shared data.

I suggest using a Semaphone in win32, though I guess a Mutex would
potentially work too. Using a semaphore you can extend at a later date to
allow mutiple locks.

HTH

M





More information about the fpc-pascal mailing list