I'm writing a multi-threaded application in Delphi and need to use something to protect shared resources.
In C# I'd use the "lock" keyword:
private someMethod() {
lock(mySharedObj) {
//...do something with mySharedObj
}
}
In Delphi I couldn't find anything similar, I found just TThread.Synchronize(someMethod) method, which prevents potential conflicts by calling someMethod in main VCL thread, but it isn't exactly what I want to do....
Edit: I'm using Delphi 6
(Un)fortunately you cannot lock on arbitrary objects in Delphi 6 (although you can in more recent versions, 2009 and later), so you need to have a separate lock object, typically a critical section.
TCriticalSection (note: the documentation is from FreePascal, but it exists in Delphi as well):
Example code:
type
TSomeClass = class
private
FLock : TCriticalSection;
public
constructor Create();
destructor Destroy; override;
procedure SomeMethod;
end;
constructor TSomeClass.Create;
begin
FLock := TCriticalSection.Create;
end;
destructor TSomeClass.Destroy;
begin
FreeAndNil(FLock);
end;
procedure TSomeClass.SomeMethod;
begin
FLock.Acquire;
try
//...do something with mySharedObj
finally
FLock.Release;
end;
end;
There is no equivalent in Delphi 6. As of Delphi 2009, you can use the System.TMonitor
methods to grab locks on arbitrary objects.
System.TMonitor.Enter(obj);
try
// ...
finally
System.TMonitor.Exit(obj);
end;
(You need the "System" prefix because the TMonitor name conflicts with the type in the Forms unit. The alternative is to use the global MonitorEnter
and MonitorExit
functions.)
Although not entirely as easy as c#, following might work for you.
with Lock(mySharedObj) do
begin
//...do something with mySharedObj
UnLock;
end;
In a nutshell
Lock(mySharedObj)
, the internal list will be searched for an existing lock. A new lock will be created if no existing lock is found. The new thread will be blocked if another thread still has the lock. Unlock
is necessary because we can not be sure that the reference to the ILock instance only will get out of scope at the end of the method calling Lock
. (If we could, the Unlock
could be removed).Note that in this design, one TLock gets created for every object instance you wish to protect without it being freed until the application terminates.
This could be factored in but it would involve messing around with _AddRef & _Release.
unit uLock;
interface
type
ILock = interface
['{55C05EA7-D22E-49CF-A337-9F989006D630}']
procedure UnLock;
end;
function Lock(const ASharedObj: TObject): ILock;
implementation
uses
syncobjs, classes;
type
_ILock = interface
['{BAC7CDD2-0660-4375-B673-ECFA2BA0B888}']
function SharedObj: TObject;
procedure Lock;
end;
TLock = class(TInterfacedObject, ILock, _ILock)
private
FCriticalSection: TCriticalSection;
FSharedObj: TObject;
function SharedObj: TObject;
public
constructor Create(const ASharedObj: TObject);
destructor Destroy; override;
procedure Lock;
procedure UnLock;
end;
var
Locks: IInterfaceList;
InternalLock: TCriticalSection;
function Lock(const ASharedObj: TObject): ILock;
var
I: Integer;
begin
InternalLock.Acquire;
try
//***** Does a lock exists for given Shared object
for I := 0 to Pred(Locks.Count) do
if (Locks[I] as _ILock).SharedObj = ASharedObj then
begin
Result := ILock(Locks[I]);
Break;
end;
//***** Create and add a new lock for the shared object
if not Assigned(Result) then
begin
Result := TLock.Create(ASharedObj);
Locks.Add(Result);
end;
finally
InternalLock.Release;
end;
(Result as _ILock).Lock;
end;
{ TLock }
constructor TLock.Create(const ASharedObj: TObject);
begin
inherited Create;
FSharedObj := ASharedObj;
FCriticalSection := TCriticalSection.Create;
end;
destructor TLock.Destroy;
begin
FCriticalSection.Free;
inherited Destroy;
end;
procedure TLock.Lock;
begin
FCriticalSection.Acquire;
end;
function TLock.SharedObj: TObject;
begin
Result := FSharedObj;
end;
procedure TLock.UnLock;
begin
FCriticalSection.Release;
end;
initialization
Locks := TInterfaceList.Create;
InternalLock := TCriticalSection.Create;
finalization
InternalLock.Free;
Locks := nil
end.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With