Riaan Moll
2010-09-20 09:22:59 UTC
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
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