У меня есть простой потокобезопасный контейнерный класс. Он имеет стандартные методы добавления/удаления. Обычно перечисление элементов реализуется как:
MyList.lock;
try
// looping here
finally
MyList.unlock;
end;
Но я хочу воспользоваться поддержкой for-in потокобезопасным способом:
for item in MyList do
begin
// do something
end;
Моя реализация перечислителя блокирует контейнер в его конструкторе и разблокирует его в деструкторе. Это работает, но основано на предположении, что экземпляр Enumerator создается в начале цикла for-in и уничтожается в конце. Я нашел это объяснение здесь: Как создается Enumerator с for в строительстве уничтожено?
Но поскольку блокировка/разблокировка является критической операцией, мне интересно, допустимо ли такое использование?
Вот моя реализация:
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;