Delphi - 有没有相当于C#锁?

时间:2010-06-11 13:11:30

标签: c# delphi multithreading delphi-6

我正在Delphi中编写一个多线程应用程序,需要使用一些东西来保护共享资源。

在C#中,我使用“lock”关键字:

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

在Delphi中我找不到类似的东西,我发现只有TThread.Synchronize(someMethod)方法,它通过在主VCL线程中调用someMethod来防止潜在的冲突,但这不是我想做的......

编辑:我正在使用Delphi 6

5 个答案:

答案 0 :(得分:17)

(联合国)幸运的是你无法锁定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;

答案 1 :(得分:11)

在Delphi 6中没有等价物。从Delphi 2009开始,您可以使用System.TMonitor方法来获取任意对象的锁定。

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

(您需要“System”前缀,因为TMonitor名称与Forms单元中的类型冲突。另一种方法是使用全局MonitorEnterMonitorExit函数。)

答案 2 :(得分:3)

虽然不像c#那么简单,但以下内容对您有用。

  with Lock(mySharedObj) do
  begin
    //...do something with mySharedObj
    UnLock;
  end;

简而言之

  • 为您希望保护的每个实例保留一个列表。
  • 当第二个线程cals 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.

答案 3 :(得分:0)

如上所述,对于不在本地范围之外调用且不获取任何其他锁定的短代码,您可以通过SyncObjs.TCriticalSection使用关键部分,
对于更长/更复杂的代码,您可以使用SyncObjs.TMutex,它是等待的(具有超时),如果拥有的线程死亡并且可以通过其他进程的名称共享,则不会停止。
使用这些包装器可以更轻松地更改同步层。

在所有情况下,请注意龙:my answer to Difference between the WaitFor function for TMutex delphi and the equivalent in win32 API

答案 4 :(得分:0)

使用类助手可以使用它。但不适用于旧版本。但我建议仅在XE5中使用TMonitor。因为它比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;