14

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

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

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

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

编辑:我正在使用 Delphi 6

4

5 回答 5

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;
于 2010-06-11T13:26:04.247 回答
11

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

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

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

于 2010-06-11T14:34:27.627 回答
3

虽然不像 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.
于 2010-06-11T13:45:23.593 回答
1

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

在所有情况下,请注意这里的龙:对 TMutex delphi 的 WaitFor 函数与 win32 API 中的等效函数之间的差异的回答

于 2010-06-11T13:35:41.450 回答
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;
于 2013-11-04T10:32:03.383 回答