Discussion:
TThread - why are updated property values not reflected from the perspective of a /different/ thread?
(too old to reply)
Riaan Moll
2010-09-20 09:22:59 UTC
Permalink
Hi guys,

Been wondering about this for a while now, and I was hoping somebody
could shed some light.

Let's assume you have a worker thread that performs some lengthy op,
and there is a another thread (say, the main VCL thread) that monitors
a flag which is a property of the worker thread class. Updating the
property value from the worker thread does not reflect when you call
the property getter from another thread (even if all access to the
data member is protected by a critical section), and I'd like to know
why. Please take a look at the following example:

//////////////////////////////////////////////////////////
type
TWorkThread = class(TThread)
private
fCritSect : TCriticalSection;
fCompleted : boolean;
procedure SetCompleted(value : boolean);
function GetCompleted : boolean;
protected
procedure Execute; override;
public
constructor Create; reintroduce;
destructor Destroy; override;
property Complete : boolean read GetCompleted write SetCompleted;
end;

//////////////////////////////////////////////////////////
constructor TWorkThread.Create;
begin
inherited Create(true);

FreeOnTerminate := false;
fCritSect := TCriticalSection.Create;

resume;
end;

//////////////////////////////////////////////////////////
procedure TWorkerThread.SetCompleted(value : boolean);
begin
fCritSect.Enter;
try
fCompleted := value;
finally
fCritSect.Leave;
end;
end;

//////////////////////////////////////////////////////////
function TWorkerThread.GetCompleted : boolean;
begin
fCritSect.Enter;
try
result := fCompleted;
finally
fCritSect.Leave;
end;
end;

//////////////////////////////////////////////////////////
procedure TWorkerThread.Execute;
begin
try
{ Perform some lengthy operation }
finally
Completed := true; // Critical section is used by using the setter
(and it definitely updates fCompleted).
end;
end;

The thread that invokes the worker thread (let's assume it's the main
VCL thread that needs to remain responsive) does something like this:

//////////////////////////////////////////////////////////
procedure DoSomething;
begin
{ create work thread }
with TWorkThread.Create do
try
{ and wait for it to finish with a cheap and nasty loop }
repeat
Application.ProcessMessages;
Sleep(150);
until Complete;

{ The loop never finishes - Complete NEVER becomes true, even
though the worker thread
is definitely completing and using the setter (I've verified
this), and so it's an infinite loop. WHY? }

finally
free;
end;
end;

Please do not preach to me about this code :-) , I know that the
correct way to do this sort of thing is by using message posting or
sync objects. I would just like to know why the behaviour of the
application is not as one would expect.

Any explanations would be greatly appreciated!

Thanks and regards,
Riaan
Maarten Wiltink
2010-09-20 12:15:42 UTC
Permalink
"Riaan Moll" <***@teba.co.za> wrote in message news:1bde2383-0ab8-4d84-b133-***@q18g2000vbm.googlegroups.com...

[...]
Post by Riaan Moll
Let's assume you have a worker thread that performs some lengthy op,
and there is a another thread (say, the main VCL thread) that monitors
a flag which is a property of the worker thread class. Updating the
property value from the worker thread does not reflect when you call
the property getter from another thread (even if all access to the
data member is protected by a critical section), and I'd like to know
why.
<snip code>
Post by Riaan Moll
Please do not preach to me about this code :-) , I know that the
correct way to do this sort of thing is by using message posting or
sync objects. ...
But you _are_ using synchronisation objects. That code looked exactly
right to me, except for the absence of the overridden destructor.

I can offer no explanation. Different threads have their own program
counter, but they share all data that isn't in threadvars. Instance
fields shouldn't be a problem and you're even protecting the read
accessor. I am at a loss. It's probably something completely unrelated.

Was that code pasted, or re-typed?

Groetjes,
Maarten Wiltink
a***@aol.com
2010-09-20 14:55:04 UTC
Permalink
I'm by no means an expert on threads (I'll leave that to Maarten) but
you seem to have TWorkThread.fCompleted which is get or set by
TWorkERThread methods.

Is Maartens query on copying valid, or is there similar named
variables in both threads, or have you been caught by the dreaded
"with" not with-ing.

Alan Lloyd
Maarten Wiltink
2010-09-20 20:43:10 UTC
Permalink
Post by a***@aol.com
I'm by no means an expert on threads (I'll leave that to Maarten)
I'm not, either.

Groetjes,
Maarten Wiltink
Jamie
2010-09-21 00:31:13 UTC
Permalink
Post by Riaan Moll
Hi guys,
Been wondering about this for a while now, and I was hoping somebody
could shed some light.
Let's assume you have a worker thread that performs some lengthy op,
and there is a another thread (say, the main VCL thread) that monitors
a flag which is a property of the worker thread class. Updating the
property value from the worker thread does not reflect when you call
the property getter from another thread (even if all access to the
data member is protected by a critical section), and I'd like to know
//////////////////////////////////////////////////////////
type
TWorkThread = class(TThread)
private
fCritSect : TCriticalSection;
fCompleted : boolean;
procedure SetCompleted(value : boolean);
function GetCompleted : boolean;
protected
procedure Execute; override;
public
constructor Create; reintroduce;
destructor Destroy; override;
property Complete : boolean read GetCompleted write SetCompleted;
end;
//////////////////////////////////////////////////////////
constructor TWorkThread.Create;
begin
inherited Create(true);
FreeOnTerminate := false;
fCritSect := TCriticalSection.Create;
resume;
end;
//////////////////////////////////////////////////////////
procedure TWorkerThread.SetCompleted(value : boolean);
begin
fCritSect.Enter;
try
fCompleted := value;
finally
fCritSect.Leave;
end;
end;
//////////////////////////////////////////////////////////
function TWorkerThread.GetCompleted : boolean;
begin
fCritSect.Enter;
try
result := fCompleted;
finally
fCritSect.Leave;
end;
end;
//////////////////////////////////////////////////////////
procedure TWorkerThread.Execute;
begin
try
{ Perform some lengthy operation }
finally
Completed := true; // Critical section is used by using the setter
(and it definitely updates fCompleted).
end;
end;
The thread that invokes the worker thread (let's assume it's the main
//////////////////////////////////////////////////////////
procedure DoSomething;
begin
{ create work thread }
with TWorkThread.Create do
try
{ and wait for it to finish with a cheap and nasty loop }
repeat
Application.ProcessMessages;
Sleep(150);
until Complete;
{ The loop never finishes - Complete NEVER becomes true, even
though the worker thread
is definitely completing and using the setter (I've verified
this), and so it's an infinite loop. WHY? }
finally
free;
end;
end;
Please do not preach to me about this code :-) , I know that the
correct way to do this sort of thing is by using message posting or
sync objects. I would just like to know why the behaviour of the
application is not as one would expect.
Any explanations would be greatly appreciated!
Thanks and regards,
Riaan
Well, I copied the code and corrected the declarations you made, you
have a TworkThread and a TworkerThread..
I had to make one of the other on my end to get it to even compile. If
it compiles on your end, this only means you have another thread you're
getting mixed up in...

In any case, it works fine on my end when I use the members in the
same thread class..

Have a good day and may your errors be many..

Loading...