How to protect and access nested objects in multithreaded application - multithreading

I have an object with nested objects. Simple diagram of object organziation would be:
TMainObj
-TState
-TDescriptor
-List<TSubObj>
--TSubObj_n
---TSubObjState
---TSubObjDesc
---TSubObjData
type TState = class
end;
type TDesc = class
end;
type TSubObjState = class
end;
type TSubObjDesc = class
end;
type TSubObjData = class
end;
type TSubObj = class
FSubObjState: TSubObjState;
FSubObjDesc: TSubObjDesc;
FSubObjData: TSubObjData;
end;
type TListSubObj = class (TList<TSubObj>)
end;
type TMainObj = class
FState: TState;
FDesc: TDesc;
FList: TList<TSubObj>
end;
I have multithreaded app and I have to enable access to objects and their properties (which are not included in this example code). Some threads share same objects some not but still they could share some properties with main thread therfore I need to protect data. I am protecting data with critical sections / mutexes. However I don't know how to organize locking mechanism in this scheme to get the best out of it.
My initial idea was to implement lock/unlock on TMainObj and whenever any thread needs to access any property or subobjects it will lock complete TMainObj and all other threads will need to wait until TMainObj becomes unlocked. For that reason I think this not really good idea. Some threads doesn't need to access TMainObj's properties but only it's sub object such as TState. I assume there is no need to lock whole TMainObj but only TState or am I missing something?
If I need to access properties on TMainObj I would do it:
TMainObj.Lock
try
TMainObj.Name := 'Just name!';
TManiObj.Id := 1;
finally
TMainObj.Unlock;
end;
And every other thread will have to wait to gain an access.
But what when I need to access sub class TDescriptor? I can do it like that:
TMainObj.Lock
try
TMainObj.Descriptor.DataLen := 1024;
TManiObj.Descriptor.Count := 10;
finally
TMainObj.Unlock;
end;
And complete TMainObj will be locked. And all other threads need to wait even if they are not 'interested' in changing TMainObj's properties.
Or that way to lock only sub object descriptor:
Thread1:
TMainObj.Descriptor.Lock
try
TMainObj.Descriptor.DataLen := 1024;
TManiObj.Descriptor.Count := 10;
finally
TMainObj.Descriptor.Unlock;
end;
Meanwhile some other thread can still access TMainObj properties and change them, right?
Thread2:
TMainObj.Lock;
try
TMainObj.Name := 'New name!';
finally
TMainObj.Unlock;
end;
Here is the image which shows how and what each thread is accessing.
One of concerns is a deadlock situation. In next case I would like to show how different threads are accessing different "part" of MainObj.
MainThread:
MainObj.Lock;
try
MainObj.Name = 'Different name!'
MainObj.Id = 2;
finally
MainObj.Unlock;
end;
Meanwhile thread1 is doing this:
MainObj.Descriptor.Lock;
try
MainObj.Descriptor.DataLen = 1024;
MainObj.Descriptor.Count = 1;
finally
MainObj.Descriptor.Unlock;
end;
So both are sharing MainObj but each is changing own part of object. Is that approach of locking appropriate?
I hope I explained my problem as clear as possible. My question is how to protect access to such object structure from different threads? Do I have to protect each subobject with it's own lock/unlock pair methods (and critical section)?

You could use TMonitor for this without adding anything to your objects. In that case your code would look like this:
TMonitor.Enter(MainObj.Descriptor);
try
MainObj.Descriptor.DataLen := 1024;
MainObj.Descriptor.Count := 10;
finally
TMonitor.Exit(MainObj.Descriptor);
end;
Provided all threads (and the main thread) that are trying to access descriptor do the same thing then they will lock waiting for the next thread to finish.
You will need to watch out for deadlocks but from what you say it shouldn't be a problem. A deadlock will occur if you do something like this:
Main Thread
Lock MainObj
Lock MainObj.Descriptor (waits for thread 1)
If thread 1 comes along and does this:
Thread 1
Lock MainObj.Descriptor
Lock MainObj (waits for main thread)

Related

Reducing overhead when accessing objects used by thread

A thread is looping through a list of 1000 objects every second.
The objects hold simple configuration data. When some conditons are met, a worker thread is given the configuration data and does some work based on that.
Now I want to bring up a settings dialog with the configuration data so I can change the data inside such an object. But then I have to access objects in the list while the thread is also continously accessing them. I know how to use a critical section, but if the thread enters a critical section each time it checks on an object, then the critical section will be entered and left 1000 times per second. Maybe there is a smarter way?
How to make threadsafe with the least overhead when:
a) loading the config data into the settings dialog form (which uses a TListView in virtual mode and needs to access the list of objects on demand)
b) and saving the form input back to the object?
EDIT: More detail was requested.
The objects are in a TList and basically look like this:
TConfigData = class
ID:Integer;
Name: String;
SwitchTime: TDateTime;
end;
The data of the ConfigData object needs to be loaded into the Settings Dialog form so it can be edited, and then, if the user clicks OK, the ConfigData object should be updated and the thread will happily use this new data next time the obkect is accessed. However, updating must not happen at the same time as the thread is reading the ConfigData object.
EDIT 2: Additional details:
The threads are reading ID, Name and SwitchTime but only SwitchTime is changed by threads. (When work is done, new time is calculated and thats what triggers next work event).
The settings dialog can change both Name and SwitchTime, but not ID.
After a bit of thought, you can get away without using critical sections at all just by using InterlockedExchangePointer:
You need to add a routine to update the config for an item:
procedure TMyThread.UpdateConfig(const aIndex: Integer; const aID:Integer;
const aName: String; const aSwitchTime: TDateTime);
var
newConfigToEdit: TConfigData;
oldValue: TConfigData;
begin
newConfigToEdit := TConfigData.Create;
newConfigToEdit.ID := aID;
newConfigToEdit.Name := aName;
newConfigToEdit.SwitchTime := aSwitchTime;
repeat
oldvalue := InterlockedExchangePointer(FConfigDataList.List[aIndex], newConfigToEdit);
until (oldvalue <> nil) and (oldValue <> newConfigToEdit);
oldvalue.Free; // Free the replaced one
end;
That routine would replace the config for the item with Index of aIndex. To get the config in your thread you will need to be a bit clever. We get a copy of it and replace the value in the list with nil while we are working on it. This prevents the other thread from replacing it. Once we have finished we put back the replaced value.
procedure TMyThread.Execute;
var
configToUse: TConfigData;
begin
repeat
// Get the config and replace it with nil so it won't be changed
configToUse := InterlockedExchangePointer(FConfigDataList.List[idx], nil);
// Do stuff with the config
// Put the config back
FConfigDataList.List[idx] := configToUse;
// You could also use the line below instead of the assignment
// InterlockedExchangePointer(FConfigDataList.List[idx], configToUse);
until Terminated;
end;
If you want to kick off a worker thread with the config then you should make a clone of it and then pass in the clone to the worker because it can be changed.
Main thread (pseudo)code (ObjList is the global variable):
if ConfigUpdated(i) then
ObjList[i].ConfigVersion := ObjList[i].ConfigVersion + 1;
Other thread(s) (pseudo)code (ObjConfVer is the local for thread)
for i := 0 to ObjCount - 1 do
if ObjConfVer[i] < ObjList[i].ConfigVersion then
begin
// Here you have to take care that main thread will can not change config while you handling it
// Do something about new config
ObjConfVer[i] := ObjList[i].ConfigVersion;
// Exit from critical section
end;
If you have n threads which works with the same ObjList it is allow to each thread doing something about changed config independently.
BTW if you are using FPC/Lazarus this link may be usefull: Parallel procedures

Delphi asynchronous write to TListBox from TCPServer OnExecute, TThread and TTimer [duplicate]

I want to write from multiple threads/processes to a TListBox called 'listMessages' and I have this two procedures in order to do this :
1- With adding object :
procedure Log(Msg: String; Color: TColor);
begin
listMessages.Items.AddObject(Msg, Pointer(Color));
listMessages.ItemIndex := listMessages.Items.Count -1;
end;
2- With TIdCriticalSection called protectListMessages :
procedure TMainForm.safelyLogMessage(mess : String);
begin
protectlistMessages.Enter;
try
listMessages.Items.Add(mess);
listMessages.ItemIndex := listMessages.Items.Count -1;
finally
protectListMessages.Leave;
end;
end;
Can you tell me which is best(fast + thread safe) or show me a third way to write messages to my TListBox from my threads/processes ?
Neither of your options is any good. You need to use option 3!
The point is that all access to UI controls must execute on the main thread. Use TThread.Synchronize or TThread.Queue to marshal UI code onto the main UI thread. Once you do this, the code will not need any further serialization because the very act of getting it to run on the UI thread serializes it.
The code might look like this:
procedure TMainForm.Log(const Msg: string; const Color: TColor);
var
Proc: TThreadProcedure;
begin
Proc :=
procedure
begin
ListBox1.AddItem(Msg, Pointer(Color));
ListBox1.ItemIndex := ListBox1.Count-1;
end;
if GetCurrentThreadId = MainThreadID then
Proc()
else
TThread.Queue(nil, Proc);
end;
In your update you state that you need to write to the list box from a different process. This cannot be achieved with any of the code in the question. You need inter-process communication (IPC) for that. Sending windows messages would be a reasonable approach to take, but there are other IPC options available. But I think that you mis-speak when you use the term process. I suspect that you don't mean process, but what you do mean, I have no idea.

Delphi asynchronous write to TListBox

I want to write from multiple threads/processes to a TListBox called 'listMessages' and I have this two procedures in order to do this :
1- With adding object :
procedure Log(Msg: String; Color: TColor);
begin
listMessages.Items.AddObject(Msg, Pointer(Color));
listMessages.ItemIndex := listMessages.Items.Count -1;
end;
2- With TIdCriticalSection called protectListMessages :
procedure TMainForm.safelyLogMessage(mess : String);
begin
protectlistMessages.Enter;
try
listMessages.Items.Add(mess);
listMessages.ItemIndex := listMessages.Items.Count -1;
finally
protectListMessages.Leave;
end;
end;
Can you tell me which is best(fast + thread safe) or show me a third way to write messages to my TListBox from my threads/processes ?
Neither of your options is any good. You need to use option 3!
The point is that all access to UI controls must execute on the main thread. Use TThread.Synchronize or TThread.Queue to marshal UI code onto the main UI thread. Once you do this, the code will not need any further serialization because the very act of getting it to run on the UI thread serializes it.
The code might look like this:
procedure TMainForm.Log(const Msg: string; const Color: TColor);
var
Proc: TThreadProcedure;
begin
Proc :=
procedure
begin
ListBox1.AddItem(Msg, Pointer(Color));
ListBox1.ItemIndex := ListBox1.Count-1;
end;
if GetCurrentThreadId = MainThreadID then
Proc()
else
TThread.Queue(nil, Proc);
end;
In your update you state that you need to write to the list box from a different process. This cannot be achieved with any of the code in the question. You need inter-process communication (IPC) for that. Sending windows messages would be a reasonable approach to take, but there are other IPC options available. But I think that you mis-speak when you use the term process. I suspect that you don't mean process, but what you do mean, I have no idea.

Delphi - accesing non UI units from inside a thread

I have the following situation.
We develop in DelphiXE.
We are putting the majority of our functions in a DATAMODULE.
function1 (database, transaction, paramInteger) : float
for example
function1 take parameters database (TIBDATABASE), the transaction TIBTRANSACTIOn and aditiona parameter integer. and return a float
function GetLastPretAch(DIBase : TIBDatabase; Tran : TIBTransaction; const aID : Integer) : Double;
var workQuery : TIBQuery;
begin
try
workQuery := TIBQuery.Create(Application);
try
workQuery.Close;
workQuery.Database := DIBase;
workQuery.Transaction := Tran;
workQuery.SQL.Clear;
workQuery.SQL.Add('SELECT * FROM GETLASTPRETACH(-1, :AARTNR)');
workQuery.ParamByName('AARTNR').AsInteger := aID;
workQuery.Open;
Result := workQuery.FieldByName('LASTPRET').AsFloat;
except
on e : Exception do begin
raise EMagisterException.Create(TranslateIbError(e));
end;
end;
finally
FreeAndNil(workQuery);
end;
end;
Now I want to use this functions from a thread. is this thread safe?
inside execute procedure like
ID := GetLastPretAch(database, transaction, 1);
is or not thread safe?
The answer to your question is Yes, you can use that function from inside a worker thread's execute procedure. You might want to consider refining your SQL to only SELECT the field LASTPRET instead of SELECT *.
For an extended discussion on what "thread safe" means refer to this SO question
What does threadsafe mean?
Looks like you're using IBX Components which, the last time I looked were most definitely NOT thread-safe. If you switched to a data access layer that was thread-safe, you should be fine with that code. FYI UIB (Unified Interbase components) are thread-safe.

How to access thread and its components?

I create a thread
type
ss_thread = class;
ss_thread = class(TThread)
protected
Fff_id : string;
Fff_cmd : string;
Fff_host : string;
Fff_port : TIdPort;
procedure Execute; override;
public
constructor Create(const ff_id, ff_cmd: string; ff_host: string; ff_port: TIdPort);
end;
constructor ss_thread.Create(const ff_id, ff_cmd: string; ff_host: string; ff_port: TIdPort);
begin
inherited Create(False);
Fff_id := ff_id;
Fff_cmd := ff_cmd;
Fff_host := ff_host;
Fff_port := ff_port;
end;
...
id := 123;
...
nst_ss_thread.Create(id, cmd, host, port);
and doing something on
procedure ss_thread.Execute;
var
ws : TIdTCPClient;
data : TIdBytes;
i : integer;
list : TList;
begin
ws := TIdTCPClient.Create(nil);
ws.Host := Fff_host;
ws.Port := Fff_port;
....
How to access this thread 'ws' variable thru another thread using id:=123 of thread ?
Thanks
It cannot.
You've declared ws as a local variable inside ss_thread.execute, which means it's only visible there. It can't be seen outside ss_thread.execute, even by other parts of ss_thread.
If you want it visible from other places or threads, you need to move it to a more visible scope. For instance, if you want it visible from other places in ss_thread, move it to the interface declaration in private or protected sections, and if you want it visible from outside ss_thread move it to the published or public sections.
You'd better not. Thread objects are exactly made to insulate its variables from other threads.
Otherwise all kind of random non-reproducible errors would appear - http://en.wikipedia.org/wiki/Heisenbug
Parallel programming should have very clear separation and insulation. Because You can never predict the timing of execution and which statement would run earlier and which one later.
Imagine that easy scenario:
ws := TIdTCPClient.Create(nil);
ws.Host := Fff_host;
// at this point another thread gets access to ws variable,
// as You demanded - and changes it, so WS gets another value!
ws.Port := Fff_port;
How would you detect such a bug, if it happens only on client multi-processor computer under heavy load once a month ? In your workstation during debug sessions or simulation it would not be reproduced ever! How would you catch it and fix ?
As a rule of thumb, when doing parallel programming the data should be spleat into "shared immutable" and "private mutable" pieces, and when doing inter-thread communication you should - similar to inter-process communications - make some events/messages queue and pass commands and replies to/from threads, like it is done in Windows GDI or like in MPI
Then you thread would fetch "change ws variable" command from queue - in the proper moment when the change is allowed - and change it from inside. Thus you would assume control and assure that variables are only changed in that point and in that manner, that would not derail the code flow.
I suggest you to read OTL examples to see how inter-thread communication is done in more safe way that direct access to objects. http://otl.17slon.com/tutorials.htm

Resources