シンプルなスレッドセーフなコンテナ クラスがあります。標準の追加/削除メソッドがあります。通常、アイテムの列挙は次のように実装されます。
MyList.lock;
try
// looping here
finally
MyList.unlock;
end;
しかし、スレッドセーフな方法で for-in サポートを利用したい:
for item in MyList do
begin
// do something
end;
私の列挙子の実装は、コンテナーをコンストラクターでロックし、デストラクターでロックを解除します。これは機能しますが、列挙子のインスタンスが for-in ループの最初に作成され、最後に破棄されるという前提に基づいています。ここでその説明を見つけました: How is Enumerator created with for in construction destroy?
でも施錠・解錠は大事な操作なので、こういう使い方でいいのかな?
これが私の実装です:
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;