How may i make global cache thread safe

631 Views Asked by At

My application used to be mono thread, but now to increase performance we need to make it multithread.

We have Lists and ListItems in the following architecture:

TBListItem = class(TBusinessObjects)
private 
  FList : TBList;
protected
  //methods
public 
  constructor Create(AList: TBList); reintroduce;
  //other methods
  //properties... 
end;

Instead of inheritance we prefer the composition of the list.

TBList = class(TPersistent)
private 
  FItemClass : TBListItemClass; //class of TBListItem

  //this is used to AddObject(key, object) of the TStringList duplicates are no allowed   
  function  BuildKey(ArrayOfKeys: array of Variant): string;
protected
  //we use a stringlist to save the items
  FList: TStringList; 

  function  GetItem(Index: Integer): TBListItem;
  //methods like Load(); Unload(); Save();
public 
  constructor Create(ARefClassItem: TBListItemClass); reintroduce;

  //these methods use buildkey
  function Add(ArrayOfKeys: Array of Variant): TBListItem; reintroduce;
  function FindByKey(const ArrayOfKeys: array of Variant): TBListItem;

  //other methods
  //properties... 
end;

In the ADD() method do this:

var Index: Integer;
begin   
  Index:= FList.IndexOf(BuildKey(ArrayOfKeys));    
  if Index <> -1 then
    Result:= TBListItem(FList.Objects[Index])
  else 
  begin        
    Result:= FListItemClass.Create(Self);
    Result.FCodigo:= ArrayOfKeys;
    //load data from database.
    FList.AddObject(BuildKey(ArrayOfKeys), Result)
  end;    
end;

As i said, this objects are used to record cache data in runtime, but everytime we need to read/write objects in it, we have to:

EnterCriticalSection(instance of TRTLCriticalSection);
//Do Stuff
LeaveCriticalSection(Same instance);

I can't change much the architeture because there are inumerous classes that inherited from that.
When i run the processes, there are a lot of spikes in the processor graphic and a lot of downtimes too.
The system is compiled from delphi 6 compiler.
The Critical session has been created in the initialization of the unit.

Is there another way to do this ?
May I somehow at least not to lock the reading ?

Also, I have to guarantee the integrity, 2 object with the same key are not allowed.

1

There are 1 best solutions below

1
On BEST ANSWER

You are going to need to perform some synchronisation on reading. You can't let one thread mutate a data structure whilst another tries to read it. A common approach is a single writer, multiple reader lock.

Delphi comes with one of these, namely TMultiReadExclusiveWriteSynchronizer. However, I believe that its performance is poor, and since the TMonitor debacle I personally have little faith in the ability of Emba's engineers to write correct synchronization primitives.

My recommendation would be to use the Slim Reader/Writer (SRW) Lock introduced in Vista. If you still need to support XP then I'd suggest falling back to a critical section.