Delphi - есть ли какой-нибудь эквивалент C # блокировки? - PullRequest
13 голосов
/ 11 июня 2010

Я пишу многопоточное приложение на Delphi, и мне нужно что-то использовать для защиты общих ресурсов.

В C # я бы использовал ключевое слово "lock":

private someMethod() {
    lock(mySharedObj) {
        //...do something with mySharedObj
    }
}

В Delphi я не смог найти ничего подобного, я нашел только метод TThread.Synchronize (someMethod), который предотвращает возможные конфликты, вызывая someMethod в основном потоке VCL, но это не совсем то, что я хочу сделать ... .

Редактировать: я использую Delphi 6

Ответы [ 5 ]

17 голосов
/ 11 июня 2010

(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;
11 голосов
/ 11 июня 2010

В Delphi 6 нет эквивалента. Начиная с Delphi 2009, вы можете использовать методы System.TMonitor для захвата блокировок произвольных объектов.

System.TMonitor.Enter(obj);
try
  // ...
finally
  System.TMonitor.Exit(obj);
end;

(Вам нужен префикс «System», потому что имя TMonitor конфликтует с типом в модуле Forms. Альтернативой является использование глобальных MonitorEnter и MonitorExit функции.)

3 голосов
/ 11 июня 2010

Хотя это не так просто, как 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.
0 голосов
/ 04 ноября 2013

Используя класс помощников, вы можете использовать это.Хотя не будет работать со старыми версиями.Но я бы посоветовал использовать 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;
0 голосов
/ 11 июня 2010

Как уже говорилось, для короткого кода, который не вызывает вне локальной области и не получает никаких других блокировок, вы можете использовать критические секции через SyncObjs.TCriticalSection,для более длинного / более сложного кода вы можете использовать SyncObjs.TMutex, который является ожидаемым (с таймаутом), не останавливается, если владелец потока умирает, и может передаваться по имени другим процессам.Использование этих оболочек облегчает изменения в слое синхронизации.

Во всех случаях остерегайтесь драконов здесь: мой ответ Разница между функцией WaitFor для TMutex delphi и эквивалентом в win32 API

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...