Delphi - Есть ли эквивалент блокировки С#?
Я пишу многопоточное приложение в Delphi и должен использовать что-то для защиты общих ресурсов.
В С# я бы использовал ключевое слово "lock":
private someMethod() {
lock(mySharedObj) {
//...do something with mySharedObj
}
}
В Delphi я не мог найти ничего подобного, я нашел метод TThread.Synchronize(someMethod), который предотвращает возможные конфликты, вызывая someMethod в основном потоке VCL, но это не совсем то, что я хочу сделать....
Изменить: я использую Delphi 6
Ответы
Ответ 1
(Un), к счастью, вы не можете блокировать произвольные объекты в 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;
Ответ 2
В Delphi нет эквивалента 6. С Delphi 2009 вы можете использовать методы System.TMonitor
для захвата блокировок на произвольных объектах.
System.TMonitor.Enter(obj);
try
// ...
finally
System.TMonitor.Exit(obj);
end;
(Вам нужен префикс "Система", потому что имя TMonitor конфликтует с типом в модуле Forms. Альтернативой является использование глобального MonitorEnter
и MonitorExit
.)
Ответ 3
Хотя это не так просто, как С#, следующие могут работать для вас.
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.
Ответ 4
Как сказано, для короткого кода, который не вызывается за пределами локальной области и не получает никаких других блокировок, вы можете использовать критические разделы через SyncObjs.TCriticalSection
,
для более длинного/более сложного кода вы можете использовать SyncObjs.TMutex
, который является необходимым (с таймаутом), не останавливается, если собственный поток умирает и может использоваться совместно с другими процессами.
Использование этих оболочек облегчает внесение изменений в уровень синхронизации.
Во всех случаях остерегайтесь драконов здесь: мой ответ на Разницу между функцией WaitFor для TMutex delphi и эквивалентом в win32 API
Ответ 5
Используя помощники классов, вы можете использовать это. Однако не будет работать со старыми версиями. Но я бы посоветовал использовать 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;