Locking and Unlocking mechanism in Delphi
Locking and Unlocking mechanism in Delphi
This article provides a mechanism to temporarily lock and unlock some aspects of a class. We will discuss locking and unlocking patterns in Delphi with a simple example.
Often you’ll find that a object dispatches notifications as a result of changing it’s internal state. Clients will handle these notifications to synchronise with the object. If multiple changes are to be applied at once, this will result in multiple notifications and subsequent synchronisations. The lock pattern let’s you temporarily lock an aspect of the class, avoiding unneeded notifications. Locking can be applied nested.
Consider for example a TBag class which implements collection like behaviour. TBag dispatches an OnChange event each time something changes. If we wanted to add multiple objects to a bag, this would result in multiple change notifications. A GUI control wiring the OnChange event would have to be repainted each time, resulting in a poor performance. The lock pattern enables locking the bag before adding the objects. When the objects are added, the bag is unlocked again, which will result in a single dispatch of OnChange. The advantage of using a lock pattern rather than setting a Boolean flag, for example FUpdating, is that calls to Lock and Unlock can be nested.
Implementation
The implementation for the lock pattern applied to a class TBag is: (only pattern related code is shown)
type
TBag = class (TObject)
private
FLockCnt: Integer;
protected
function Locked: Boolean;
procedure SetLocking(Updating: Boolean);
public
procedure Lock;
procedure UnLock;
end;
implementation
procedure TBag.Lock;
begin
Inc(FLockCnt);
if FLockCnt = 1 then SetLocking(False);
end;
function TBag.Locked: Boolean;
begin
Result := (FLockCnt <> 0);
end;
procedure TBag.SetLocking(Updating: Boolean);
begin
end;
procedure TBag.UnLock;
begin
Dec(FLockCnt);
if FLockCnt = 0 then SetLocking(True);
end;
In this implementation notice: The field FLockCnt which stores the state of the locking mechanism. FLockCnt = 0 represents an unlocked state. Any other value implicates a locked state. This allows nested calls to Lock and Unlock which are the only methods changing this field. The methods Lock and Unlock which provide the locking interface. Each time a call to one of these methods causes a locked state change, method SetLocking is called. Method SetLocking has one parameter Updating. If Updating is True, the bag has become unlocked due to a call to Unlock. If Updating is False, the bag has become locked due to a call to Lock. You would typically insert some code in this method which deals with the lock state change. Method Locked returns the state of the locking mechanism.
Notice how calls to Lock and Unlock always must be paired to avoid the Bag to remain locked forever. Therefore it is wise to use a try..finally block to make sure pairs are always matched, as is demonstrated in the following example.
A typical use of the lock mechanism would be:
procedure TBag.Add(Item: Pointer);
begin
{ Add Item to internal structure }
Change;
end
procedure TBag.AddItems(Items: TList);
begin
Lock;
{ Add multiple items }
try
for I := 0 to Items.Count - 1 do
Add(Items[I]);
finally
{ use try..finally to make sure Unlock is called }
Unlock;
end;
end;
procedure TBag.Change;
begin
if not Locked then
if Assigned(FOnChange) then FOnChange(Self);
end;
procedure TBag.SetLocking(Updating: Boolean);
begin
if Updating then { Bag has become unlocked }
Change;
end;
Since this mechanism can be used in many situations, the lock patterns let’s you edit the patterns field and method names. This pattern is one of the few patterns that can be applied to the same class more than once and still be meaningful. You would of course need different names, such as LockUpdate, LockScreenUpdate or BeginUpdate.