1

我有简单的线程安全容器类。它具有标准的添加/删除方法。通常枚举项目被实现为:

MyList.lock;
try
  // looping here
finally
  MyList.unlock;
end;

但我想以线程安全的方式利用 for-in 支持:

for item in MyList do 
begin
  // do something
end;

我的枚举器实现将容器锁定在它的构造函数中,并在析构函数中解锁它。这是可行的,但前提是 Enumerator 的实例是在 for-in 循环的开头创建并在最后被销毁的。我在这里找到了解释:Enumerator created with for in construction 是如何被破坏的?

但是由于锁定/解锁是一项关键操作,我想知道这种用法是否可以?

这是我的实现:

  TContainer<T> = class
    private
      FPadLock: TObject;
      FItems: TList<T>;
    protected
    public
      type
        TContainerEnumerator = class(TList<T>.TEnumerator)
          private
            FContainer: TContainer<T>;
          public
            constructor Create(AContainer: TContainer<T>);
            destructor Destroy; override;
        end;
      constructor Create;
      destructor Destroy; override;
      procedure add(AItem: T);
      procedure remove(AItem: T);
      function GetEnumerator: TContainerEnumerator;
  end;

{ TContainer<T> }

procedure TContainer<T>.add(AItem: T);
begin
  TMonitor.Enter(FPadLock);
  try
    FItems.Add(AItem);
  finally
    TMonitor.Exit(FPadLock);
  end;
end;

constructor TContainer<T>.Create;
begin
  inherited Create;
  FPadLock := TObject.Create;
  FItems := TList<T>.Create;
end;

destructor TContainer<T>.Destroy;
begin
  FreeAndNil(FItems);
  FreeAndNil(FPadLock);
  inherited;
end;

procedure TContainer<T>.remove(AItem: T);
begin
  TMonitor.Enter(FPadLock);
  try
    FItems.Remove(AItem);
  finally
    TMonitor.Exit(FPadLock);
  end;
end;

function TContainer<T>.GetEnumerator: TContainerEnumerator;
begin
  result := TContainerEnumerator.Create(self);
end;

{ TContainer<T>.TContainerEnumerator }

constructor TContainer<T>.TContainerEnumerator.Create(
  AContainer: TContainer<T>);
begin
  inherited Create(AContainer.FItems);
  FContainer := AContainer;
  TMonitor.Enter(FContainer.FPadLock);  // <<< Lock parent container using Monitor
end;

destructor TContainer<T>.TContainerEnumerator.Destroy;
begin
  TMonitor.Exit(FContainer.FPadLock);  // <<< Unlock parent container
  inherited;
end;
4

1 回答 1

2

枚举器在 for 循环开始时创建,并在循环结束时销毁。枚举数的生命周期由 try/finally 管理。

不过,不要只相信我的话。添加一些调试代码很容易,这些代码将检测您的循环并让您查看何时调用析构函数。

这意味着您提出的锁定策略是合理的。

我想说虽然在堆上分配一个枚举器,当你有线程争用时,可能会导致性能问题。

于 2014-10-23T09:58:32.417 回答