Critical section is one way to protect resources against access by multiple threads. The Windows API allows you to create such sections by using the WinAPI. Find more about it here.

Delphi ships with a simple class that implements a critical sections and makes them very easy to use. The following code shows everything you need to do.

uses SyncObjs;

var Critical : TCriticalSection;

procedureInit;
begin
  Crictical := TCriticalSection.Create;
end;

procedureDoJob;
begin
  Critical.Enter;
  try
    //do stuff here, that must be protected from multiple access
  finally
    Critical.Leave;
  end;
end;

procedureDone;
begin
  Critical.Free;
end;

Unfortunately there is no implementation of a “try to enter” function, so we have to do it ourselves. This is done by using the WinAPI function TryEnterCriticalSection in a new class derived from TCriticalSection.

uses JwaWindows, SyncObjs;

type
  TTryCriticalSection = Class(TCriticalSection)
  public
    function TryEnter : Boolean;
  end;

function TMyCrits.TryEnter: Boolean;
begin
  result := TryEnterCriticalSection(Self.fSection);
end;

Critical sections may become very inefficient if you have several threads which almost do only reading access to the shared resource. Of course it would be possible to have parallel readings as long as no write access is done. But critical sections always blocks access. Here comes TMultiReadExclusiveWriteSynchronizer very handy. It allows to distinguish between read and write operations. Concurrent read accesses are allowed, but not mixing up read and write access. However you have to either use BeginRead or BeginWrite, depending on what your action will be .

uses SysUtils;
var Lock : TMultiReadExclusiveWriteSynchronizer;
    List : TStringList;

procedureInit;
begin
  Lock := TMultiReadExclusiveWriteSynchronizer.Create;
  List := TStringList.Create;
end;

procedureDoWrite;
begin
  Lock.BeginWrite;
  try
    List.Add(‘test’);
  finally
    Lock.EndWrite;
  end;
end;

functionDoRead : String;
var i : Integer;
begin
  Lock.BeginRead;
  try
    //do reading stuff on List here
    if List.Count > 0 then
      result := List[0];
  finally
    Lock.EndRead;
  end;
end;

procedureDone;
begin
  List.Free;
  Lock.Free;
end;

DoRead can be called in a thread without locking out other threads which also call DoRead/BeginRead. However as soon as DoWrite/BeginWrite is called no other thread can enter the critical section in DoRead or even DoWrite. The other way around DoWrite/BeginWrite will wait until all BeginRead/BeginWrite calls were closed by EndRead/EndWrite.
It is even possible to check for changes while waiting for the lock to be removed.

function BeginWrite: Boolean;

The method BeginWrite returns false if the lock was activated (and released) by another thread that called BeginWrite before. In this case the content of the resources is probably changed and thus is no more the same as before the call BeginWrite happened. This may be a relevant issue if you can’t ignore the changed content.

Tell me how you liked this blog entry by adding a comment.