How to safely access and modify an array while multithreading? - multithreading

I'm trying to manipulate a variable of type array of record, by multiple threads, and I'm not sure whether what i did is the right way, or if there is a better and safer method?
I declared a boolean variable as a lock, and when some thread want to access the array it waits till the lock is off, then activate the lock, and when done, unlock it and let others have access.
The code for this is declared in the implementation section
...
implementation
var Data : array of TData;
var Data_Lock:Boolean=false;
procedure Lock_Data();
begin
while Data_Lock = True do
sleep(1);
Data_Lock := True;
end;
procedure UnLock_Data();
begin
Data_Lock := False;
end;
procedure ClearAll();
begin
Lock_Data();
SetLength( Data, 0 );
UnLock_Data();
end;
....
The entire project is still not complete. For now this seems to work but i don't have any knowledge of how these things work at the core, and whether there would be a problem if two threads start at the exact same time?

Your locking approach is not thread safe and it will not protect your data.
With multiple threads you have to consider that any particular thread execution can be interrupted at any time and that another thread can "jump" in and access some variable in between.
That means following scenario is possible (simplified):
Data_Lock is False
Thread A enters Lock_Data()
Thread A checks Data_Lock -> False and skips the loop
Thread B enters Lock_Data()
Thread B checks Data_Lock -> False and skips the loop (Thread A didn't have the chance to set it to True yet)
Thread A continues -> sets Data_Lock to True and gains access to protected data
Thread B continues -> sets Data_lock to True and gains access to protected data while Thread A is still using that data
You can use TCriticalSection from System.SyncObjs instead.
var DataLock: TCriticalSection;
procedure ClearAll();
begin
DataLock.Enter;
try
SetLength(Data, 0);
finally
DataLock.Leave;
end;
end;
Since TCriticalSection is a class you need to create DataLock instance before you can use it and you need to free it when you no longer need it. For instance, you can do that in initialization/finalization section of a unit.
initialization
DataLock := TCriticalSection.Create;
finalization
DataLock.Free;
end.
However, the better approach would be to wrap your data and critical section together in a class.

Related

How to make multiple threads do work on a single TStringList

I want to be able to make multiple threads do work on a single TStringList. This is my current Thread code for using a single thread to do the work;
type
TFilterThread = class(TThread)
protected
procedure Execute; override;
public
lCombos : TStringList;
//Public Vars
end;
procedure TFilterThread.Execute;
var
I: integer;
HTML: String;
frm1 : TForm1;
splitLogin: TStringList;
validCount: Integer;
begin
validCount := 0;
for I := 0 to lCombos.Count - 1 do
begin
if Terminated then Exit();
Unit1.Form1.ListBox1.ItemIndex := i;
try
HTML := Unit1.Form1.IdHTTP1.Get(EmailCheckURL + lCombos[i]);
if AnsiPos('You indicated you', HTML) > 0 then
begin
//Do stuff
end;
except
Continue;
end;
end;
Showmessage('Finished!');
Unit1.Form1.Button1.Caption := 'Start';
end;
To Start the thread I use;
lComboLsit := TStringList.Create;
for I := 0 to listBox1.Items.Count -1 do
lComboLsit.Add(listBox1.Items[i]);`
iTFilterThread := TFilterThread.Create(True);
iTFilterThread.FreeOnTerminate := True;
iTFilterThread.lCombos := lComboLsit;
iTFilterThread.Start;
How would I introduce another thread to also do work on the lCombos list so that the operation would complete quicker?
The answer is that it depends. If the string list is not modified, then there's nothing to do. If the string list is modified then what is best depends critically on your specific usage.
Looking at your code, your program may not be CPU bound and so adding more threads may not help. Your program's bottleneck will the HTTP communication. However, despite not being CPU bound it is plausible that running multiple threads will reduce the impact HTTP latency. You can benchmark to find out. You don't appear to be modifying the string list so there's no race problems to be concerned with there.
However, here's a problem:
Unit1.Form1.IdHTTP1.Get(EmailCheckURL + lCombos[i]);
That will work, and is thread safe so long as TIdHTTP is thread safe. But it's pretty ugly to allow a thread to access a component on a form like that. And I don't see any real sense or need to share the TIdHTTP instance between threads. It would be far cleaner to let each thread instantiate and use their own TIdHTTP component.
Of course, you will need to decide on a policy for dividing the work between all your threads. You could have a shared index that keeps track of the next item to process. Have each thread increment it atomically each time they take an item. A parallel for loop would be a good fit here. That's available in the latest version of Delphi, or in any decent third party parallel library.
You do have some problems with your code. In the thread procedure you do this:
Unit1.Form1.ListBox1.ItemIndex := i;
....
Unit1.Form1.Button1.Caption := 'Start';
You cannot access VCL components from a thread.
And then you do this
ShowMessage('Finished!');
Don't show UI from a thread.
A point of idiom. Instead of looping over the items in your list box you can simply do this:
lComboLsit.Assign(listBox1.Items);

Using the Delphi XE7 Parallel Library

I have a time consuming routine which I'd like to process in parallel using Delphi XE7's new parallel library.
Here is the single threaded version:
procedure TTerritoryList.SetUpdating(const Value: boolean);
var
i, n: Integer;
begin
if (fUpdating <> Value) or not Value then
begin
fUpdating := Value;
for i := 0 to Count - 1 do
begin
Territory[i].Updating := Value; // <<<<<< Time consuming routine
if assigned(fOnCreateShapesProgress) then
fOnCreateShapesProgress(Self, 'Reconfiguring ' + Territory[i].Name, i / (Count - 1));
end;
end;
end;
There is really nothing complex going on. If the territory list variable is changed or set to false then the routine loops around all the sales territories and recreates the territory border (which is the time consuming task).
So here is my attempt to make it parallel:
procedure TTerritoryList.SetUpdating(const Value: boolean);
var
i, n: Integer;
begin
if (fUpdating <> Value) or not Value then
begin
fUpdating := Value;
n := Count;
i := 0;
TParallel.For(0, Count - 1,
procedure(Index: integer)
begin
Territory[Index].Updating := fUpdating; // <<<<<< Time consuming routine
TInterlocked.Increment(i);
TThread.Queue(TThread.CurrentThread,
procedure
begin
if assigned(fOnCreateShapesProgress) then
fOnCreateShapesProgress(nil, 'Reconfiguring ', i / n);
end);
end
);
end;
end;
I've replaced the for-loop with a parallel for-loop. The counter, 'i' is locked as it is incremented to show progress. I then wrap the OnCreateShapeProgress event in a TThread.Queue, which will be handled by the main thread. The OnCreateShapeProgress event is handled by a routine which updates the progress bar and label describing the task.
The routine works if I exclude the call to the OnCreateShapeProgress event. It crashes with an EAurgumentOutOfRange error.
So my question is simple:
Am I doing anything anything stupid?
How to do you call an event handler from within a TParallel.For loop or TTask?
The most obvious problem that I can see is that you queue to the worker thread.
Your call to TThread.Queue passes TThread.CurrentThread. That is the very thread on which you are calling TThread.Queue. I think it is safe to say that you should never pass TThread.CurrentThread to TThread.Queue.
Instead, remove that parameter. Use the one parameter overload that just accepts a thread procedure.
Otherwise I'd note that the incrementing of the progress counter i is not really handled correctly. Well, the incrementing is fine, but you then read it later and that's a race. You can report progress out of order if thread 1 increments before thread 2 but thread 2 queues progress before thread 1. Solve that by moving the counter increment code to the main thread. Simply increment it inside the queued anonymous method. Added bonus to that is you no longer need to use an atomic increment since all modifications are on the main thread.
Beyond that, this QC report seems rather similar to what you report: http://qc.embarcadero.com/wc/qcmain.aspx?d=128392
Finally, AtomicIncrement is the idiomatic way to perform lock free incrementing in the latest versions of Delphi.

Synchronize multiple threads

I'm trying to make a multithreaded application (up to lets say 100 thread) in Delphi XE5,
all the thread are going to use/change a listbox in the main form,
here is my code:
Main Unit:
private
//list to hold the dynamic created threads
SendThreads : TObjectList;
public
mutex : boolean;
...
procedure TMainForm.FormCreate(Sender: TObject);
begin
...
mutex := false;
SendThreads := TObjectList.Create;
end;
...
//Button to create and start the threads
procedure TMainForm.btnWorkClick(Sender: TObject);
var
Thread : TSendThread;
i, nbr : integer;
begin
if SendThreads.Count < spnThreadCount.Value then
begin
for I := 1 to spnThreadCount.Value - SendThreads.Count do
begin
nbr := SendThreads.Add(TSendThread.Create(true));
Thread := TSendThread(SendThreads.Items[nbr]);
Thread.FreeOnTerminate := true;
Thread.Start;
end;
end;
end;
Thread Unit:
uses MainUnit;
procedure TSendThread.Execute;
begin
QueryList;
end;
//Basically, this procedure checks if the item in the listbox contains '- Done.' which means that this
//item has been done by another thread, if not, the current thread deal with this item.
procedure TSendThread.QueryList;
var i : integer;
S : String;
begin
i := 0;
while i < MainForm.lstURL.Count do
begin
while MainForm.mutex do;
MainForm.mutex := true;
if pos(' - Done.', MainForm.lstURL.Items[i]) = 0 then
begin
S := MainForm.lstURL.Items[i];
Delete(S, 1, pos('?txt=', S) + length('?txt=') - 1);
MainForm.Memo1.Lines.Add(MainForm.lstURL.Items[i]);
MainForm.lstURL.Items[i] := MainForm.lstURL.Items[i] + ' - Done.';
MainForm.mutex := false;
SendAd(URL, S);
end
else
begin
Inc(i);
MainForm.mutex := false;
end;
end;
end;
This method works if the number of the threads are less than 4, but if it's more i get redundant result (2 or more threads do the same item).
Now i'm fairly new to threads and multithreading, and i was wondering if this is the right way to do it.
In addition to what Ken said about UI safety, you are getting threads processing the same item because you have a race condition on your mutex variable. Multiple threads may see mutex=false at the same time as they are not actually syncing with each otber. You need to use a true mutex. Look at the TMutex class. Or just wrap the majority of your code in TThread.Synchronize() and let the VCL handle the syncing for you. But then that defeats the purpose of using threads.
You are using the completely wrong design for your requirement. You need to separate your worker thread logic from your UI logic. There are a few different ways you can do that.
Put your work jobs into a thread-safe queue, like TThreadList<T> or TThreadedQueue<T>. Each thread can check the queue periodically and pull the next available job if there is one.
A. A variation is to use an I/O Completion Port as the queue. Post jobs to the IOCP using PostQueuedCompletionStatus(), and have each thread use GetQueuedCompletionResult() to receive jobs. This allows the OS to do all the queuing and pulling for you, while allowing threads to sleep when there are no jobs available.
Put your threads into a pool in a sleeping state. When a new job is ready, check the pool. If a thread is available, pull it from the pool, pass the job to it, and wake it up. Otherwise put the job in a thread-safe queue. When a job is done, have that thread check the queue. If a job is available, pull it from the queue, otherwise put the thread in the poll and put it back to sleep.
In any case, when a job is done being processed, that thread can notify the main thread of the job result, using TThread.Synchronize(), TThread.Queue(), or any other inter-thread communication of your choosing. The main thread can then update the UI as needed.
At no point should the threads touch the UI to discover new jobs.

How can I terminate my threads with blocking functions/procedures?

I use TThread in my application and I have a LOT of functions that I would like to use inside of it.
Functions that I use, take time to complete, so it's not ideal to use them in threads. That's why I was wondering if there's a way other than just copy & paste the function/procedure and then put (maybe inject) my terminated flags into the function.
I don't want to use TerminateThread API!
A short example:
procedure MyProcedure;
begin
// some work that takes time over a few lines of code
// add/inject terminated flag?!
// try... finally...
end;
procedure TMyThread.Execute;
begin
MyProcedure;
// or copy and paste myprocedure
end;
So is there an efficient way to write procedures/functions that help me with the terminated flag? Also the procedures/functions should be global so other functions/procedures can call them too.
One option is to introduce a callback method into your procedure call.
If the callback method is Assigned (when called from a thread) make the call and take action.
When calling MyProcedure from elsewhere, pass nil to the procedure.
Type
TAbortProc = function : boolean of object;
procedure MyProcedure( AbortProc : TAbortProc);
begin
//...
if (Assigned(AbortProc) and AbortProc) then
Exit;
//...
end;
function MyThread.AbortOperation : Boolean;
begin
Result := Terminated;
end;
The reason why I avoid passing the thread reference instead of a callback method, is to hide the thread logic (and dependency) from MyProcedure.

Does TCriticalSection allow multithreaded access to a variable?

I want to syncronize threads to access a common variable.
Imagine that I have N threads and each of them can acess a global instance of a variable of type TSyncThreds.
Can I call the methods IncCount, DecCount? Or i will have problem with concurrent threads accessing a same instance of a object?
I just syncronize the access to the FCcounter Variable...
type
TSyncThreads = class
public
FCounterGuard: TCriticalSection;
FCounter: integer;
FSimpleEvent: TSimpleEvent;
constructor Create();
procedure Wait();
procedure IncCounter();
procedure DecCounter();
end;
var
SyncThread: TSyncThreads;
implementation
uses
Math, Windows, Dialogs;
{ TSyncThreads }
procedure TSyncThreads.DecCounter;
begin
FCounterGuard.Acquire;
Dec(FCounter);
if FCounter = 0 then
FSimpleEvent.SetEvent;
FCounterGuard.Release;
end;
procedure TSyncThreads.IncCounter;
begin
FCounterGuard.Acquire;
Inc(FCounter);
FCounterGuard.Release;
end;
constructor TSyncThreads.Create();
begin
FCounter := 0;
FSimpleEvent := TSimpleEvent.Create;
FCounterGuard := TCriticalSection.Create;
FSimpleEvent.ResetEvent;
end;
procedure TSyncThreads.Wait;
var
ret: TWaitResult;
begin
ret := FSimpleEvent.WaitFor(INFINITE);
end;
Yes, your code is fine, if a bit overkill for the task at hand. You're worried about multiple threads accessing the same object, but that's exactly what synchronization objects like TCriticalSection and TEvent are designed for. Imagine how pointless such classes would be if they couldn't be accessed concurrently.
You don't really need a critical section to protect access to your counter. You can use InterlockedIncrement and InterlockedDecrement for more lightweight access. They return the variable's previous value, so if InterlockedDecrement returns 1, then you know it's time to set your event.
If there's an upper bound on the counter, then you might even be able to avoid all of this code and use a semaphore instead. Or you can give each thread its own event to set, and then use WaitForMultipleObjects to wait on all the events; it will return once all the threads have set their events, so you don't have to synchronize access to any shared variable.

Resources