Delphi - есть ли какой-нибудь эквивалент C# блокировки?
Я пишу многопоточное приложение на Delphi, и мне нужно что-то использовать для защиты общих ресурсов.
В C# я бы использовал ключевое слово "lock":
private someMethod() {
lock(mySharedObj) {
//...do something with mySharedObj
}
}
В Delphi я не смог найти ничего подобного, я нашел только метод TThread.Synchronize(someMethod), который предотвращает потенциальные конфликты, вызывая someMethod в основном потоке VCL, но это не совсем то, что я хочу сделать....
Редактировать: я использую Delphi 6
5 ответов
(Не), к счастью, вы не можете заблокировать произвольные объекты в Delphi 6 (хотя вы можете сделать это в более поздних версиях, 2009 и более поздних), поэтому вам нужно иметь отдельный объект блокировки, обычно критическую секцию.
TCriticalSection (примечание: документация от FreePascal, но она существует и в Delphi):
Пример кода:
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;
В Delphi 6 нет эквивалента. Начиная с Delphi 2009, вы можете использовать System.TMonitor
методы захвата замков на произвольных объектах.
System.TMonitor.Enter(obj);
try
// ...
finally
System.TMonitor.Exit(obj);
end;
(Вам нужен префикс "System", потому что имя TMonitor конфликтует с типом в модуле Forms. Альтернативой является использование глобального MonitorEnter
а также MonitorExit
функции.)
Хотя это не так просто, как в C#, вам может помочь следующее.
with Lock(mySharedObj) do
begin
//...do something with mySharedObj
UnLock;
end;
В двух словах
- список хранится для каждого экземпляра, который вы хотите защитить.
- когда вторая нить вызывает
Lock(mySharedObj)
, во внутреннем списке будет выполнен поиск существующей блокировки. Новая блокировка будет создана, если существующая блокировка не найдена. Новый поток будет заблокирован, если другой поток все еще имеет блокировку. Unlock
необходимо, потому что мы не можем быть уверены, что ссылка на экземпляр ILock только выйдет из области видимости в конце вызова методаLock
, (Если бы мы могли,Unlock
может быть удален).
Обратите внимание, что в этом проекте один TLock создается для каждого экземпляра объекта, который вы хотите защитить, без его освобождения до завершения работы приложения.
Это может быть учтено, но это будет связано с возни с _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.
Как уже говорилось, для короткого кода, который не вызывает вне локальной области и не получает никаких других блокировок, вы можете использовать критические секции через SyncObjs.TCriticalSection
,
для более длинного / более сложного кода вы можете использовать SyncObjs.TMutex
, который является ожидаемым (с тайм-аутом), не останавливается, если владелец потока умирает, и может быть разделен по имени с другими процессами.
Использование этих оболочек облегчает изменения уровня синхронизации.
Во всех случаях остерегайтесь драконов здесь: мой ответ на разницу между функцией WaitFor для TMutex delphi и аналогом в Win32 API
Используя помощников класса вы можете использовать это. Не будет работать со старыми версиями, хотя. Но я бы посоветовал использовать TMonitor только в XE5. Так как это намного медленнее, чем TRTLCriticalSection.
http://www.delphitools.info/2013/06/06/tmonitor-vs-trtlcriticalsection/
THelper = class helper for TObject
procedure Lock;
procedure Unlock;
end;
procedure THelper.Lock;
begin
System.TMonitor.Enter(TObject(Self));
end;
procedure THelper.Unlock;
begin
System.TMonitor.Exit(TObject(Self));
end;