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
Related
Some background:
Basically it comes down to that I want to be able to "execute" the task in the current thread.
Why? -I have a task creator routine, and one time I want the task to be executed immediately in a background task, and at other times I want the task to be scheduled using an IOmniThreadPool.
In the case that I want to use the OmniThreadpool I want do do some output about the task's status, like that it was queued, started, finished or failed.
The task's execution is not yet started at this moment.
So I imagined someting like this:
function WrapIntoPoolTask(const aOriginalTask:IOmniTaskCOntrol):IOmniTaskCOntrol;
begin
if Assigned(aOriginalTask) then
begin
var lPoolProgress:=TaskPoolProgress; // fetch global task pool progress interface
// immediately displays message says its been queued, remember message bookmark
var lMessageBookMark:=lPoolProgress.MessageList.AddMessage(aOriginalTask.Name,pmPaused);
Result:=CreateTask(
procedure (const aTask:IOmniTask)
begin
lPoolProgress.MessageList.UpdateMessage(lMessageBookMark,pmStarted); // update message status
try
aOriginalTask.ExecuteTaskInThisThread; // <<=== So how do I do this?
lPoolProgress.MessageList.UpdateMessage(lMessageBookMark,pmCompleted);
except
lPoolProgress.MessageList.UpdateMessage(lMessageBookMark,pmFailed);
raise;
end;
end
,
'Pooled:'+aOriginalTask.Name
);
end;
end;
Using the UpdateMessage call after performing the original task can be moved to the OnTerminated handler of the IOmniTaskControl interface. I tried that and it works just fine for the thread ending part. It even allows for handling exit codes and exit messages which I like even better.
I think what I am missing here is probably an OnInitialize or OnStartExecution handler to set my pmStarted status.
Question:
How can I directly execute a tasks "body" from IOmniTaskCOntrol
or
how can I add some initialization code to my task after the task was already created. This code should be executed immediatliy prior to executing my tasks original body.
In order to solve my problem I had to change the omnithreadlibrary unit OtlTaskControl a bit.
one routine added to IOmniTaskControl (GUID should change, but I didn'tt)
IOmniTaskControl = interface ['{881E94CB-8C36-4CE7-9B31-C24FD8A07555}']
...
function DirectExecute:IOmniTaskControl;
...
end; { IOmniTaskControl }
And the implementation added to TOmniTaskControl:
function TOmniTaskControl.DirectExecute:IOmniTaskControl;
VAR lTask:IOmniTask;
begin
Result:=self;
lTask:=CreateTask;
(lTask as IOmniTaskExecutor).Execute;
end;
Then my "custom wrapper" routine that actually adds the pool progress handling to whatever the original task was:
function WrapIntoOmniPoolTask(const aTaskControl:IOmniTaskCOntrol):IOmniTaskCOntrol;
var lTaskControl:IOmniTaskCOntrol;
lPoolProgress:IAppProgress;
lmbm:integer;
begin
if Assigned(aTaskControl) then
begin
// have some local copies to work around compiler bug RX10.3 and RX10.4
// cannot use inline vars due to this bug either.
lTaskControl:=aTaskControl;
lPoolProgress:=TaskPoolProgress;
lmbm:=lPoolProgress.MessageList.AddMessage(aTaskControl.Name,pmPaused);
Result:=CreateTask(
procedure (const aTask:IOmniTask)
begin
try
lPoolProgress.MessageList.UpdateMessage(lmbm,pmStarted);
try
lTaskControl.DirectExecute;
aTask.SetExitStatus(lTaskControl.ExitCode,lTaskControl.ExitMessage);
HandlePoolTaskTermination(lTaskControl,lmbm,lPoolProgress);
except
HandlePoolTaskTermination(lTaskControl,lmbm,lPoolProgress);
if IsFatalException then
raise;
end;
finally
// release interfaces when all is done
lPoolProgress:=nil;
lTaskControl:=nil;
end;
end,
'Pooled: '+lTaskCOntrol.Name
);
end;
end;
And finally, the routine that schedules my wrapped task into the omnipoolthread.
function TfrmMain.CreateTestTask:IOmniTaskControl;
begin
Result:=WrapIntoOmniPoolTask(CreateTask(TestTask,TGUID.NewGuid.ToString)).Unobserved;
end;
Everything seems to work as expected including the exit code and exit message which are propagated from the inner task to the outer task.
The compiler bug I am referring to is reported here: https://quality.embarcadero.com/browse/RSP-29564 (please vote!)
For those interested: this is what HandlePoolTaskTermination looks like:
procedure HandlePoolTaskTermination(const aTaskControl:IOmniTaskCOntrol;const aMessageBookmark:integer;const aPoolProgress:IAppProgress);
begin
var pm:=pmCompleted;
if Assigned(aTaskControl.FatalException) then
begin
pm:=pmWarning;
var pe:=peError;
if IsAbortException(aTaskControl.FatalException) then
pe:=peWarning
else if IsFatalException(aTaskControl.FatalException) then
begin
pm:=pmFailed;
pe:=peFatalError;
end;
aPoolProgress.ErrorList.AddErrorToMessage(aMessageBookmark,'',pe,aTaskControl.FatalException)
end
else if aTaskControl.ExitCode<>0 then
begin
pm:=pmWarning;
aPoolProgress.ErrorList.AddErrorToMessage(aMessageBookmark,aTaskControl.ExitMessage,peWarning);
end;
aPoolProgress.MessageList.UpdateMessage(aMessageBookmark,pm);
end;
The IsFatalException returns true if the "current" exception is eg EAccessViolation, EInvalidOperation and alike.
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)
A TreeView item and data holds a filename and the data holds a TBitmap. My question pertains to somehow saving the items and the data in the treeview so the items and data can be accessed in a thread. If this is possible, after saving items and data, I could access the items and data in the thread rather than accessing it in Synchronize. As the code written now is too slow because of accessing the GUI TreeItems and Data in Synchronize.
if not Terminated then
begin
Synchronize(
procedure
var
i: integer;
begin
for i := 1 to Form1.TreeView1.Items.Count - 1 do
begin
{ get the bitmap }
iImageEnIO.WIAParams.ProcessingBitmap := iImageEnIO.IEBitmap;
{ The following line prevents me from accessing the TreeView data in a thread }
iImageEnIO.WIAParams.Transfer(TIEWiaItem(Form1.TreeView1.Items[i].Data), False);
{ Set the filename }
iFilename := Form1.TreeView1.Items[i].Text + '.jpg';
{ Add the image to the iIEImageList }
iIndex := iIEImageList.AppendImageRef(TIEBitmap.Create(iImageEnIO.IEBitmap), iFileName);
iIEImageList.Filename[iIndex] := iFileName;
end;
end);
The threads code to access the bitmaps in the thread itself works very well, but if I can move the code that gets the bitmap to the thread rather than in Synchronize would be much better. So my question is "Is there ay way to save a treeview items and data in Synchronize so it can be accessed in the thread outside of Synchronize"?
iImageEnIO.OnProgress := ImageEnProcProgress;
iImageEnIO.OnFinishWork := ImageEnProcFinishWork;
{ Get the bitmap from the imagelist and save the image in the thread }
iCount := iIEImageList.ImageCount;
for j := 0 to iCount-1 do
begin
{ get the filename from the string list }
iFilename := iIEImageList.Filename[j];
{ attach the iIEBitmap2 to iImageEnIO }
iImageEnIO.AttachedIEBitmap := iIEImageList.Image[j];
iPath := IncludeTrailingPathDelimiter(iFolder) + iFilename;
iImageEnIO.SaveToFile(iPath);
end;
I hope I have asked my question correctly and it is clear what I would like to try to do.
I would like to turn this on its head. You want to know how to read the data from the GUI control when executing your thread method. That is a fundamental design flaw. The solution to your problem will involve not attempting to do that at all.
The tree view should not be the owner of the data. It is a GUI control and should merely present a view of the data. The data should be held in a structure that is not bound to the VCL threading rules. Once you have the data structure separated from the GUI, your problem becomes trivial. Once you reach that point, there is no problem to be solved.
So, what kind of a structure do you need? Although it is stored in a tree view, it appears to be a flat list. Store it in a TList<T> container. What do you use for T? Well, that's just the information needed for each item. That could be a record. Or it could be a class. If you are holding non-value type objects in the item type, then a class is probably better. In which case TObjectList<T> is a better fit. So, it would look like this:
type
TItem = class
private
FFileName: string;
FBitmap: TBitmap;
end;
Then your container is simply TObjectList<TItem>. Instantiate it like this:
FItems := TObjectList<TItem>.Create(True);
The True is for the OwnsObjects parameter. This means that when you delete items from the container, or delete the container, the items are destroyed.
At this point you are able to populate the tree view by iterating over the container, and creating nodes to represent the items.
Then when your thread needs to operate on the data, it can refer to FItems which is separated from the GUI control.
The moral of the story is that you should not use your GUI controls as your primary data containers.
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
I have never used threads--never thought my code would benefit. However, I think threading might improve performance of the following pseudo code:
Loop through table of records containing security symbol field and a quote field
Load a web page (containing a security quote for a symbol) into a string variable
Parse the string for the quote
Save the quote in the table
Get next record
end loop
Loading each web page takes the most time. Parsing for the quote is quite fast. I guess I could take, say, half the records for one thread and work the other half in a second thread.
In OmniThreadLibrary it is very simple to solve this problem with a multistage pipeline - first stage runs on multiple tasks and downloads web pages and second stage runs in one instance and stores data into the database. I have written a blog post documenting this solution some time ago.
The solution can be summed up with the following code (you would have to fill in some places in HttpGet and Inserter methods).
uses
OtlCommon,
OtlCollections,
OtlParallel;
function HttpGet(url: string; var page: string): boolean;
begin
// retrieve page contents from the url; return False if page is not accessible
end;
procedure Retriever(const input: TOmniValue; var output: TOmniValue);
var
pageContents: string;
begin
if HttpGet(input.AsString, pageContents) then
output := TPage.Create(input.AsString, pageContents);
end;
procedure Inserter(const input, output: IOmniBlockingCollection);
var
page : TOmniValue;
pageObj: TPage;
begin
// connect to database
for page in input do begin
pageObj := TPage(page.AsObject);
// insert pageObj into database
FreeAndNil(pageObj);
end;
// close database connection
end;
procedure ParallelWebRetriever;
var
pipeline: IOmniPipeline;
s : string;
urlList : TStringList;
begin
// set up pipeline
pipeline := Parallel.Pipeline
.Stage(Retriever).NumTasks(Environment.Process.Affinity.Count * 2)
.Stage(Inserter)
.Run;
// insert URLs to be retrieved
for s in urlList do
pipeline.Input.Add(s);
pipeline.Input.CompleteAdding;
// wait for pipeline to complete
pipeline.WaitFor(INFINITE);
end;
If the number of records is relatively small, say 50 or less, you could just launch a separate thread for each record and let them all run in parallel, eg:
begin thread
Load a web page for symbol into a string variable
Parse the string for the quote
Save the quote in the table
end thread
.
Loop through table of records
Launch a thread for current security symbol
Get next record
end loop
If you have a larger number of records to process, consider using a pool of threads so you can handle records in smaller batches, eg:
Create X threads
Put threads in a list
Loop through table of records
Wait until a thread in pool is idle
Get idle thread from pool
Assign current security symbol to thread
Signal thread
Get next record
end loop
Wait for all threads to be idle
Terminate threads
.
begin thread
Loop until terminated
Mark idle
Wait for signal
If not Terminated
Load a web page for current symbol into a string variable
Parse the string for the quote
Save the quote in the table
end if
end loop
end thread
There are many different ways you could implement the above, which is why I left it in pseudocode. Look at the VCL's TThread, TList, and TEvent classes, or the Win32 API QueueUserWorkerItem() function, or any number of third party threading libraries.