worker thread updating an object created in main thread

I am having some problems with a simple main thread + single worker thread application .. they may be due to my logic or perhaps to a misunderstanding of thread communication. I cannot reduce the problem to a simple example as yet, so I am looking for conceptual feedback.

I have a worker thread .. TworkerThread=class(Tthread). I want this to automatically FreeOnTerminate and it is convenient for me to have the thread tell another object (TWatcher=class(Tcomponent) ) about what it is doing, where it (the thread) has got to, what the status is. The watcher is owned by the form and survives the thread.

So I instantiate the Watcher in the caller (the VCL) and then set a property of the thread (eg property MyWatcher:Twatcher) to point to it
before resuming the thread. In the Execute method, MyWatcher properties (eg MyWatcher.CasesProcessed) are updated.

MyWatcher is only freed by the form, long after the thread has died.

There is only ONE worker thread per Watcher. Only the WorkerThread can update the Watcher and the main (VCL) thread only ever queries it.

My assumption here is that because the WorkerThread and the MainThread share the same address space, that whenever the WorkerThread alters a property of the Watcher, the updated Watcher properties will be available in the MainThread (whenever that gets switched to).

This is the crux of the problem. Can I be sure that any object that is instantiated in the main thread and updated in a worker thread (whch knows the pointer to that object)  will be updated immediately? And hence will be accessible in its updated state to the worker thread and the main thread?

My architecture is, I think, simple. A MainThread, a Watcher instantiated in the MainThread and a pointer to that Watcher passed to the WorkerThread : the WorkerThread directly sets and gets properties of the Watcher, the MainThread examines them as needed.

looking for some elucidation here .. is my mental model wrong? can a thread update properties of an object it points to without issues of time delay and thread context switching?

I need conceptual help, and soon.
Who is Participating?

[Product update] Infrastructure Analysis Tool is now available with Business Accounts.Learn More

I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

One thing you have to watch for is corruption of data caused by two threads trying to update the same data at once. For this you should use critical sections, mutexes, or other forms of protection. There is no reason why you can't do what you've said, but just remember the issues.

Geoff M.

Look at this simple example using a label on a form in a thread. The example uses Synchronize method. As Geoff said you could use messages, critical section, etc. instead.

unit Unit1;


  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

  TForm1 = class(TForm)
    btnCreateThread: TButton;
    btnTerminateThread: TButton;
    Label1: TLabel;
    btnShowLabel: TButton;
    procedure btnTerminateThreadClick(Sender: TObject);
    procedure btnCreateThreadClick(Sender: TObject);
    procedure btnShowLabelClick(Sender: TObject);
    { Private declarations }
    { Public declarations }

  TTransactionThread = class(TThread)
    MyLabel: TLabel;
    procedure Execute; override;
    procedure Oops;
    c: integer;

  Form1: TForm1;
  TransactionThread: TTransactionThread;


{$R *.DFM}

// Thread code

procedure TTransactionThread.Oops;
  MyLabel.Caption := 'Pass '+IntToStr(c);

procedure TTransactionThread.Execute;
  k: integer;
 k := 0;
 c := 0;
 while (not terminated) and (k <> 5) do begin
   if k = 5 then begin
     // the actual code goes here (loop with delete)
     k := 0;

// Form code

procedure TForm1.btnTerminateThreadClick(Sender: TObject);

procedure TForm1.btnCreateThreadClick(Sender: TObject);
  TransactionThread := TTransactionThread.Create(true);
  TransactionThread.FreeOnTerminate := true;
  TransactionThread.MyLabel := Form1.Label1;

procedure TForm1.btnShowLabelClick(Sender: TObject);




Regards, Geo
Wim ten BrinkSelf-employed developerCommented:
You still need to synchronize with the watcher thread if the worker wants to update watcher properties, especially when modifying string properties, pointers or child objects. Delphi's synchronization method like Geo points out should work nicely, except for one issue. Geo is synchronizing with the main thread and you actually need to synchronize with the Watcher thread. So you might need some inter-thread communications instead. Probably means using mutexes, events or semaphores, possibly combined with sending messages between threads.
I had to solve a similar situation once for a scheduler service. I needed one thread who was just using for user input, a second thread keeping an eye on the clock ans starting applications at certain times and a third thread that would start and stop executables. The whole combination had to be as stable as possible, was not allowed to eat away too much resources and should be able to run unsupervised for weeks, even months. I managed to do this but it involved adding a lot of synchronisizing between all the threads that were running. WaitForMultipleObject combined with a short timeout is quite useful too in these cases. So are critical sections. Use them or risk data corruption...

And yes, even a boolean value can be corrupted. I discovered that the hard way since I used a boolean property in my project. What happened was that the set method was called and halfway the execution, the other thread would be activated, call the get method, notice it wasn't set while it was actually about to be set and my system would crash because it was looking for resources that were freed when I tried to set that boolean property...
The only types of data that you can reasonable safely access in multi-threaded applications are single-byte variables. Even a 2-byte variable can be corrupted because a thread could stop while only one of the two bytes has been changed, and the other is still unchanged. Leads to many, nasty bugs.
Python 3 Fundamentals

This course will teach participants about installing and configuring Python, syntax, importing, statements, types, strings, booleans, files, lists, tuples, comprehensions, functions, and classes.

Hi Alex,

Will there be a different thread for the Watcher? It is a component on a form, i.e. part of the VCL thread, isn't it? The worker thread needs to change a property of that component, so Synchronize with the VCL should be enough, I think.

Regards, Geo
Mutley2003Author Commented:

ALEX said

The only types of data that you can reasonable safely access in multi-threaded applications are single-byte variables. Even a 2-byte variable can be corrupted because a thread could stop while only one of the two bytes has been changed, and the other is still unchanged. Leads to many, nasty bugs

The SDK says
PlatformSDK:DLLs, Processes, and Threads

Interlocked Variable Access
The interlocked functions provide a simple mechanism for
synchronizing access to a variable that is shared by multiple
threads. The threads of different processes can use this
mechanism if the variable is in shared memory.

Simple reads and writes to properly-aligned 32-bit variables are
atomic. In other words, when one thread is updating a 32-bit
variable, you will not end up with only one portion of the
variable updated; all 32 bits are updated in an atomic fashion.
However, access is not guaranteed to be synchronized. If two
threads are reading and writing from the same variable, you
cannot determine if one thread will perform its read operation
before the other performs its write operation.

Even if the variables are "properly aligned" (and I am not sure that
Delphi DOES align fields in an object on 32 bit boundaries ... anyone?), I read this as saying that you STILL cannot trust updating
even a simple global integer variable from the VCL and a worker thread, or even more simply, updating it in the worker thread and reading it in the VCL thread (which is what I wanted to do). This is because there is no guarantee of synchronized access.

OK, do people agree with me that this is unsafe with a simple global integer?

Then how about a property that has just simple fields for access (ie no getters and setters) .
property Status : integer read fstatus write fstatus

Is this any more unsafe? (as in attempting to write it from a worker thread and "subsequently" read it from the VCL thread

Alex's example of a setter on a Boolean property being corrupted I can now understand given that I have started to think about context switching happening 18 times per second.

But would the following code fix it?

procedure TtransactionThread.TurnMyBooleanOff;
form1.MyBoolean := false;

procedure TtransactionThread.TurnMyBooleanOn;
form1.MyBoolean := true;

procedure TtransactionThread.SetMyBoolean(const Val:Boolean);
if val then synchronize(TurnMyBooleanOn) else

It looks like it should work, but it sure is ugly (the procedure called by Synchronize cannot have any arguments).

Now, what if I want to READ form1.MyBoolean in the TransactionThread?

I could introduce a new field in TTransactionThread as
private __MyBoolean : Boolean

and another 3 methods

procedure TTransactionThread.__getMyBoolean;
__MyBoolean :=form1.MyBoolean;

procedure TTransactionThread.getMyBoolean;

function TTransactionThread.MyBoolean :Boolean;
result := __MyBoolean;

which is truly ugly. I am not even sure it will work... it seems as if you are updating a thread local field __MyBoolean by running in the VCL thread.

Is synchronize just a 1 way street then ?

I noticed you put in


but with FreeOnTerminate :=True, surely TransactionThread might be
already freed at this time?

This leads me to the issue of managing thread lifetimes, one of the reasons I built my Watcher component (so I could query it about the thread even though the thread might have been freed).

What is the consensus about using FreeOnTerminate?

I have been setting it as True mostly because of lack of a sensible place to do the freeing. in Finalization is one possibility, so I guess is in the OnTerminate event handler - but the problem from my point of view is that the code  looks disjointed .. you start the thread in one procedure and do the cleanup and access to results somewhere else.

thanks for the help, people.


Mutley2003Author Commented:
I have looked a bit further at this and there is an interesting discussion
re what is and is not atomic (if atomic == 1 machine isntruction and cannot be interrupted by task switching).

The consensus appears to be that simple 32 bit assignment is atomic, but not much else. But the issue was also raised that even though the operation is atomic the result might be stored in a processor cache and maybe that would have an effect (possibly an effect on synchronization). There is a C++ keyword volatile to force stuff straight to memory, but I don't know the Delphi equivalent.

The suggestion was made to always use mutexes when accessing shared data. I thought the idea of mutexes was like critical sections when WRITING data .. I am still unclear as to how mutexes might be used in Delphi to ensure synchronization.
I think you may need to use critical sections if you want to pass parameters, which Synchronize doesn't allow. So, your program could look something like this:

TMyClass = class
  fMyString : string;
  function GetMyString : string;
  procedure SetMyString(s : string);
  property MyString : string read GetMyString write SetMyString;

function TMyClass.GetMyString : string;
  Result := fMyString;

procedure TMyClass.SetMyString(s : string);
  fMyString := s;

Now you can read and write MyClass.MyString without worrying about corruption. Note that you need to create and destroy the MyLock (critical section) variable.

Geoff M.
hello Mutley2003, For me I would always try and use some sort of thread syncro just to be sure, althouh if you are only doing a single 32 bit value assignment you might can get by without it. I consider a Mutex if there is More than one process to access some shared memory. . . For a single process (what you have described) There is a very handy Delphi utility called TMultiReadExclusiveWriteSynchronizerin SysUtils, for muti thread access control, the delphi help says -

Use TMultiReadExclusiveWriteSynchronizer to guard access to memory in a multi-threaded application.
Unlike a critical section, which blocks all other threads from reading or writing its associated memory,
TMultiReadExclusiveWriteSynchronizer allows multiple threads to read from the protected memory simultaneously,
while ensuring that any thread writing to the memory has exclusive access.

you can set up a variable for your TMultiReadExclusiveWriteSynchronizer for each Thread so each thread only accesses a single TMultiReadExclusiveWriteSynchronizer. . . .

FLock: TMultiReadExclusiveWriteSynchronizer;

and somewhere in yout code before you need to use it create the TMultiReadExclusiveWriteSynchronizer object . . . .

FLock := TMultiReadExclusiveWriteSynchronizer.Create;

you will need to Free it also, since it does not have a owner;

now before you READ or WRITE to any Thread-Shared variable, call FLock.BeginWrite; or FLock.BeginRead; and then call FLock.EndWrite; or FLock.EndRead; , , , probally in a try and finally block

  Thread-SharedObj.Value := 55;
  Thread-SharedVar := NewValue;

 - - - - - - - - - - - - - -  - - - -

  aValue := Thread-SharedObj.Value;
  NewValue := Thread-SharedVar;

- - - - - - - - - - - - - - - - - - - - -  -
that should do it for ya
the help also says

In applications where threads read from an object or variable frequently and only write to it occasionally,
using the multi-read exclusive-write synchronizer instead of a critical section can result in considerable performance improvement.

All access to the protected memory must be bracketed by calls to the BeginRead and EndRead or
BeginWrite and EndWrite methods. Any thread that reads from or writes to this memory without using
these calls can introduce thread conflicts.

This seems like a good way to do what you want and ensure acurrate reads with thread safety

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Mutley2003Author Commented:
I am going to close this off because I have had enough help from you people to get me to the next stage.. I needed some education and some pointers, I got that, I think I can use critical sections to do what I need, and most importantly I feel much more comfortable and in control of what I am doing. I'll post again when I get to the next stage of difficulty <g>. So long, and thanks for all the fish.
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today

From novice to tech pro — start learning today.