Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

100 USD for thread pool component

12 views
Skip to first unread message

Robert Smaze

unread,
May 14, 2008, 6:22:40 AM5/14/08
to
Hi guys,

I know this is very unusual but I'm a bit despaired.

A couple of years ago and came across a component called TMultiTasker. This
component was exact what I was looking for and I used it for my program. In
the past years this component became a central function in my software and I
can't without it anymore.

Right now I'm still using C++ Builder 5 but I have to change to new version
soon. Now I noticed that this component does not work in CBuilder 6 and
above. I can install it but when I compile and a program I get an "Access
violation..." error.

I don't think it is an C++ Builder issue because I found out that people
reported the same issue with Delphi 6 and above.

So I'm not a delphi expert and I looked over it but I didn't see what's
wrong. I also tried to covert it to a C++ component but it didn't go well
either. And, of course I tried to contact the author as well. But no luck.

So all my hope is on you guys. I'm willing to pay up to 100 bucks if anyone
could fix this component. Either as delphi or (better) C++ Builder component
for version 6 and above.

The component can be found on
http://www.delphi-files.com/download/vcl/system/threads/multitasker.zip.
It's freeware.

Thank you very much.

Rob

P.S. This is not a joke or spam. I'm serious.

Martin James

unread,
May 14, 2008, 8:16:46 AM5/14/08
to

> either. And, of course I tried to contact the author as well. But no luck.
>
> So all my hope is on you guys. I'm willing to pay up to 100 bucks if
> anyone could fix this component. Either as delphi or (better) C++ Builder
> component for version 6 and above.
>
> The component can be found on
> http://www.delphi-files.com/download/vcl/system/threads/multitasker.zip.
> It's freeware.
>

I looked briefly at the code. I'm afraid that I'd pay $200 to not look at
it again. The sight of 'TThread.suspend' was enough. I strongly suggest
that you no longer use this package as-is, even if you can get it working on
D6/whatever. Even the author has obviously had problems with 'suspend',
'suspended' etc. and I'm not surprised.

Controlling thread operation with suspend/resume is hazard-ridden.

Can you explain in more detail what you use this component for? There may
be some alternative that is more likely to work reliably.

Rgds,
Martin


Robert Smaze

unread,
May 14, 2008, 8:58:03 AM5/14/08
to
Martin James wrote:
> I looked briefly at the code. I'm afraid that I'd pay $200 to not look at
> it again. The sight of 'TThread.suspend' was enough. I strongly suggest
> that you no longer use this package as-is, even if you can get it working on
> D6/whatever. Even the author has obviously had problems with 'suspend',
> 'suspended' etc. and I'm not surprised.
>
> Controlling thread operation with suspend/resume is hazard-ridden.
>
> Can you explain in more detail what you use this component for? There may
> be some alternative that is more likely to work reliably.
>
> Rgds,
> Martin
>

Hi Martin,

what a pity. I've never experienced any problems with the component.

My goal is the following:

I monitor multiple directories (up to 100) for file changes (I'm using the
FindFirstChangeNotification function). If a file has been changed I call the
component to do some stuff in a thread. Now it might be that 20 directories
has been changed at the same time and of course I don't want to execute the
thread 20 times at the same time. So with the component It limit the threads
to 3 at a time and if one thread has been terminated the next thread will be
started. It works like a pool or queue.

A function of this component which is very useful in this context is the
LockExecution function. As soon as a thread calls LockExecution it can be
sure to execute a piece of code without sharing or interruption by any other
thread. The other threads have to wait until the first call of
UnLockExecution. I.e. I use the function to write into a log file.

I hope it is clear what I'm driving at.

Thanks Martin. Any help is really appreciated.

Rob

Remy Lebeau (TeamB)

unread,
May 14, 2008, 1:26:53 PM5/14/08
to

"Robert Smaze" <kom...@gmail.com> wrote in message
news:482ae1dc$1...@newsgroups.borland.com...

> It works like a pool or queue.

Windows has built-in thread pooling support:

Thread Pooling
http://msdn.microsoft.com/en-us/library/ms686756(VS.85).aspx

QueueUserWorkItem()
http://msdn.microsoft.com/en-us/library/ms684957(vs.85).aspx

> A function of this component which is very useful in this context
> is the LockExecution function. As soon as a thread calls
> LockExecution it can be sure to execute a piece of code without
> sharing or interruption by any other thread. The other threads have
> to wait until the first call of UnLockExecution. I.e. I use the function
> to write into a log file.

It uses a normal critical section for that. You can use one manually just
as easily.


Gambit


Martin James

unread,
May 15, 2008, 3:05:50 AM5/15/08
to

"Robert Smaze" <kom...@gmail.com> wrote in message
news:482ae1dc$1...@newsgroups.borland.com...
> Martin James wrote:
>> I looked briefly at the code. I'm afraid that I'd pay $200 to not look
>> at it again. The sight of 'TThread.suspend' was enough. I strongly
>> suggest that you no longer use this package as-is, even if you can get it
>> working on D6/whatever. Even the author has obviously had problems with
>> 'suspend', 'suspended' etc. and I'm not surprised.
>>
>> Controlling thread operation with suspend/resume is hazard-ridden.
>>
>> Can you explain in more detail what you use this component for? There
>> may be some alternative that is more likely to work reliably.
>>
>> Rgds,
>> Martin
>>
>
> Hi Martin,
>
> what a pity. I've never experienced any problems with the component.

Obviously, you have now <g>

Just a thought, do you have to support Wintendo? If not, could you use the
'ReadDirectoryChangesW' API? I ask this because FFCN/FNCN returns a handle
upon which thread/s can wait and, IIRC, the wait functions are limited to 63
handles. Does this mean that you need two threads to monitor 100 trees?

'ReadDirectoryChangesW' can run overlapped, so eliminating the need for more
than one thread to monitor any number of folders and also eliminating
polling. RDCW however, is not suppoted on the 16-bit OS - you need W2k
Prof, XP, Windows servers or better, (or, in the case of Vista, worse:).


> My goal is the following:
>
> I monitor multiple directories (up to 100) for file changes (I'm using the
> FindFirstChangeNotification function). If a file has been changed I call
> the component to do some stuff in a thread.

OK. Presumably a thread has to receive the notification of whatever
tree/file has changed and then do something with it?

Now it might be that 20 directories
> has been changed at the same time and of course I don't want to execute
> the thread 20 times at the same time.

So... you need to detect the changes to folders/trees and queue the changes
to a small number of threads. The threads do 'stuff' with the
folders/trees/files.

Is that right?

So with the component It limit the threads
> to 3 at a time and if one thread has been terminated the next thread will
> be started. It works like a pool or queue.

I confess I did not look too closely at the code - the sight of 'suspend'
was enough for me, (as you might have noticed:). If the component is
continually creating and terminating threads, then it's not as good as I
initially thought it was.

> A function of this component which is very useful in this context is the
> LockExecution function. As soon as a thread calls LockExecution it can be
> sure to execute a piece of code without sharing or interruption by any
> other thread. The other threads have to wait until the first call of
> UnLockExecution. I.e. I use the function to write into a log file.

Usually better to queue the log entries to one, dedicated 'logger' thread.
This is way easier to manage and the log-producer threads do not have tt
fight their way into a CS that is locked for extended periods during disk
writes/flushes/change log file/whatever. Also, if your work is based on a
class, the 'used' class can be queued to the logger thread and the logger
can call a method of it to get text to write.

>
> I hope it is clear what I'm driving at.
>

I don't need your 100 greenbacks, but if I can suggest an easier approach,
then I will.

I suspect that you may be better off queuing the work, rather than the
threads.

Rgds,
Martin


danny heijl

unread,
May 15, 2008, 3:10:08 AM5/15/08
to
Robert Smaze schreef:

> Hi guys,

Perhaps http://andy.jgknet.de/async/ could be useful?

Danny
---

Robert Smaze

unread,
May 15, 2008, 11:15:39 AM5/15/08
to
Martin James wrote:
>
> 'ReadDirectoryChangesW' can run overlapped, so eliminating the need for more
> than one thread to monitor any number of folders and also eliminating
> polling. RDCW however, is not suppoted on the 16-bit OS - you need W2k
> Prof, XP, Windows servers or better, (or, in the case of Vista, worse:).

Ah. Okay. Good point. I'll come back to this after I have the thread thing
running. This is more important right now.

>
> So... you need to detect the changes to folders/trees and queue the changes
> to a small number of threads. The threads do 'stuff' with the
> folders/trees/files.
>
> Is that right?

Exactly. This is what I'm doing.

>
> Usually better to queue the log entries to one, dedicated 'logger' thread.

Good point but how can I queue this to a thread? This is one of my problem.
I don't know how I can do this.

>
> I don't need your 100 greenbacks, but if I can suggest an easier approach,
> then I will.

Cheers, I really appreciate your help.


>
> I suspect that you may be better off queuing the work, rather than the
> threads.

What did you mean by this sentence? Sorry I'm not a native speaker and I
don't get the sentence.

Robert Smaze

unread,
May 15, 2008, 11:18:26 AM5/15/08
to

Hi Danny,

thanks for the link. I already checked and my quick look told me that this
wasn't what I was looking for. But maybe I have to take a deeper look.

Thanks.
Rob.

Martin James

unread,
May 16, 2008, 4:29:03 AM5/16/08
to
>
> Exactly. This is what I'm doing.
>
>>
>> Usually better to queue the log entries to one, dedicated 'logger'
>> thread.
>
> Good point but how can I queue this to a thread? This is one of my
> problem. I don't know how I can do this.
>
>>
>> I don't need your 100 greenbacks, but if I can suggest an easier
>> approach, then I will.
>
> Cheers, I really appreciate your help.
>>
>> I suspect that you may be better off queuing the work, rather than the
>> threads.
>
> What did you mean by this sentence? Sorry I'm not a native speaker and I
> don't get the sentence.

Look at 'How sequence treads for a TidHTTP web spider' in
b.p.d.internet.winsock - sounds like a similar task.

Continually creating/suspending/terminating threads is very inefficient.
That, plus it's usually messy, complex, difficult to get right and difficult
to debug. If your work class can be queued to a small pool of threads, it's
much easier to manage.

Rgds,
Martin


Remy Lebeau (TeamB)

unread,
May 16, 2008, 1:33:48 PM5/16/08
to

"Robert Smaze" <kom...@gmail.com> wrote in message
news:482c539b$1...@newsgroups.borland.com...

> Good point but how can I queue this to a thread? This is one of
> my problem. I don't know how I can do this.

Simply write a thread class that has a thread-safe stringlist. Other
threads can push messages into the list when needed, and the thread can loop
through the list regularly extracting the items and pushing them to the log.
For example:

type
TLogger = class(TThread)
private
Msgs: TStringList;
Lock: TCriticalSection;
protected
procedure Execute; override;
public
constructor Create; reintroduce;
destructor Destroy; override;
procedure Add(const Msg: String);
end;

constructor TLogger.Create;
begin
Msgs := TStringList.Create;
Lock := TCriticalSection.Create;
inherited Create(False);
end;

destructor TLogger.Destroy;
begin
Msgs.Free;
Lock.Free;
inherited Destroy;
end;

procedure TLogger.Add(const Msg: String);
begin
Lock.Enter;
try
Msgs.Add(Msg);
finally
Lock.Leave;
end;
end;

procedure TLogger.Execute;
var
List: TStringList;
I: Integer;
begin
List := TStringList.Create;
try
while not Terminated do
begin
Lock.Enter;
try
List.Assign(Msgs);
Msgs.Clear;
finally
Lock.Leave;
end;

if List.Count > 0 then
begin
for I := 0 to List.Count-1 do
begin
// add List[I] to log as needed...
end;
end else
Sleep(10);
end;
finally
List.Free;
end;
end;

Then the rest of your code can do this when needed:

var
Logger: TLogger = nil;

//...
Logger.Add('some line');
//...
Logger.Add('another line');
//...

initialization
Logger := TLogger.Create;

finalization
Logger.Free;


Gambit


Ian Boyd

unread,
Jul 1, 2008, 2:11:02 PM7/1/08
to
> the thread can loop through the list regularly

What do you mean by "regularly"? How do you signal the thread that there's
work to do?

> while not Terminated do
> begin

> if List.Count > 0 then
> begin
> for I := 0 to List.Count-1 do
> begin
> // add List[I] to log as needed...
> end;
> end else
> Sleep(10);
> end;

Do you really want to have a thread busy waiting the user's notebook while
running on battery, sucking up battery life, and preventing pages from being
swapped out? Do you really want to be accessing properties of a stringlist
list without locking it?


Remy Lebeau (TeamB)

unread,
Jul 1, 2008, 3:20:02 PM7/1/08
to

"Ian Boyd" <ian.borla...@avatopia.com> wrote in message
news:486a7335$1...@newsgroups.borland.com...

> What do you mean by "regularly"?

At regular/frequent intervals.

> How do you signal the thread that there's work to do?

It determines that on its own by actually looking in the list periodically
to see if there are items available. I demonstrated that in my earlier code
example.

If you really want to implement signalling, then add a TEvent into the code,
ie:

type
TLogger = class(TThread)
private
Msgs: TStringList;
Lock: TCriticalSection;

Signal: TEvent;


protected
procedure Execute; override;
public
constructor Create; reintroduce;
destructor Destroy; override;
procedure Add(const Msg: String);
end;

constructor TLogger.Create;
begin
Msgs := TStringList.Create;
Lock := TCriticalSection.Create;

Signal := TEvent.Create(nil, True, False, '');
inherited Create(False);
end;

destructor TLogger.Destroy;
begin
Msgs.Free;
Lock.Free;

Signal.Free;
inherited Destroy;
end;

procedure TLogger.Add(const Msg: String);
begin
Lock.Enter;
try
Msgs.Add(Msg);

Signal.SetEvent;
finally
Lock.Leave;
end;
end;

procedure TLogger.Execute;
var
List: TStringList;
I: Integer;
begin
List := TStringList.Create;
try

while not Terminated do
begin

while Signal.WaitFor(1000) <> wrSignaled do
Continue;

Lock.Enter;
try
List.Assign(Msgs);
Msgs.Clear;
finally

Signal.Reset;
Lock.Leave;
end;

for I := 0 to List.Count-1 do
begin
// add List[I] to log as needed...
end;

List.Clear;
end;
finally
List.Free;
end;
end;

> Do you really want to be accessing properties of a stringlist
> list without locking it?

Look at my earlier code again. There was locking involved. You stripped
that out when quoting it.


Gambit


Ian Boyd

unread,
Jul 1, 2008, 3:38:45 PM7/1/08
to
> Look at my earlier code again. There was locking involved. You stripped
> that out when quoting it.

It looked to me as though the locking was only used for protecting the list
before adding or removing items; which i understand and was was just fluff
for the question i had: how to notify the thread that items have been added.

The reason i asked that is because the OP's thread pool library is no longer
maintained; which Martin theorized because of inherit design problems around
Suspend/Resume. So i was left to wonder how you'd solve the signalling
problem without using Suspend/Resume.


0 new messages