Question

I have static array with 100 items type of record:

TMy_Array:array[1..100] of T;

where T is:

T = record
  A: double;
  B: Date;
  C: String;
end;

I have n similar threads modifying their elements in the TMy_Array

TMy_Array[n].A; 
TMy_Array[n].B; 
TMy_Array[n].C)`. 

N is close to 100.

I also have 10 other threads that selectively modify any field TMy_Array. To achieve this safely should I use critical sections and here comes the question:

Does use only one critical section will not cause overcrowding of fighting and waiting threads to access the array?

Maybe Can I or should I apply 100 critical sections that will protect access to a particular items in T_Array and the will not block other threads?

Var
  Cs:array [1..100] of TRTLCriticalSection;

//then for n-thread:
EnterCriticalSection(CS[n]);
TMy_Array[n].A:=…;
TMy_Array[n].B:=…;
TMy_Array[n].C:=…;
LeaveCriticalSection(CS[n]);

Is this a good idea, doesn't overload too much application?

And second question:

Are the TDateTime (really Double) and String atomic data? Can I read (only read) them without worry about conflicts during another thread is writing to it?

Was it helpful?

Solution

Try to serialize the access to your list/array.

  • The simplest way to serialize is by using a TThreadList for keeping the records. TThreadList exists in two versions, one generic in Generics.Collections and a non-generic in Classes. All access to such a list is guarded with a lock/unlock mechanism. This approach is a good start. Measure performance, and see if there are any problem bottlenecks.

  • Another approach is to have one thread guard all list/array accesses through a thread-safe queue. Other threads trying to read/write data from the list/array sends a read/write request on the queue.

    • For the reading request, a copy of the record is sent in another queue to the requesting thread.
    • The write request is committed by the guardian thread.

    Now everything is event driven with minimum delay. No conflicts about thread safety and a clear description on causality.

    For a thread-safe queue, look at TThreadedQueue if you have Delphi-XE2 or newer.


Here is an example outlining the above described queue approach.

Uses
  Classes,SysUtils,Generics.Collections;
Type
  T = record
    A : Double;
    B : String;
  end;
var
  MyArr : array[1..100] of T;
  GuardingQueue : TThreadedQueue<TProc>;

procedure GuardingThread.Execute;
var
  aProc : TProc;
begin
  while not Terminated do
  begin
    aProc := GuardingQueue.PopItem;
    if not Assigned(aProc) then
      Exit; // Quit thread when nil is sent to the queue
    aProc(); // Execute request
  end;
end;

procedure AccessingThread.Execute;
var
  aLocalQueue : TThreadedQueue<T>;
  aT : T;
begin
  // Make sure aLocalQueue is initialized
  // To get data fom the array ...
  GuardingQueue.PushItem( // read from array
    procedure
    var
      aT : T;
    begin
      aT.A := MyArr[2].A;
      aT.B := MyArr[2].B;
      aLocalQueue.PushItem(aT);
    end
  );
  aT := aLocalQueue.PopItem; // Here is the result from the read request

  // Writing to the array ...
  GuardingQueue.PushItem( // write to array
    procedure
    begin
      MyArr[2].A := 2;
      MyArr[2].B := 'Test';
    end
  );

end;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top