我有简单的线程安全容器类。它具有标准的添加/删除方法。 通常枚举项目实现为:
MyList.lock;
try
// looping here
finally
MyList.unlock;
end;
但我希望以线程安全的方式利用for-in支持:
for item in MyList do
begin
// do something
end;
我的枚举器实现将容器锁定在它的构造函数中,并在析构函数中解锁它。这是有效的,但假设Enumerator的实例是在for-in循环的开头创建的,并在最后被销毁。我在这里找到了解释:How is Enumerator created with for in construction destroyed?
但是,由于锁定/解锁是一项关键操作,我想知道这种用法是否合适?
这是我的实施:
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;
答案 0 :(得分:2)
枚举器是在for循环开始时创建的,并在循环结束时被销毁。枚举器的生命周期由try / finally管理。
不要只听我的话。添加一些调试代码可以很容易地修改循环,让你看到析构函数被调用的时间。
这意味着您提出的锁定策略是合理的。
我会说虽然在堆上分配一个枚举器,但是当你有线程争用时,可能会导致性能问题。