Задача: дан класс имеющий приватный TThreadList (список с thread-safe доступом) и содержащий некие объекты. Нужно написать метод предоставляющий безопасный доступ к содержимому списка, без доступа пользователя к объекту-списку.
На первый взгляд задача совсем не простая т.к. для того чтоб обеспечить безопасный доступ необходимо вызывать методы блокировки/разблокировки объекта-списка, а условия нам этого не позволяют. На помощь приходят... интерфейсы (ну если быть точным, то лишь механизм подсчета их ссылок).
Решение: Определяем интерфейс IObjectProvider, который будет возвращать наш метод.
Type
IObjectProvider = Interface
Function GetObject : TObject;
Property Object : TObject Read GetObject;
End;
Затем описываем класс TObjectProvider, который реализует описанный ранее интефейс.
Type
//
TObjectProvider = Class(TInterfacedObject, IObjectProvider)
Private
FList : TThreadList;
FObject : TObject;
Public
Constructor Create(Const AList : TThreadList; AIndex : Integer);
Destructor Destroy; Override;
Function GetObject : TObject;
End;
//
// Реализация конструктора
Begin
FList := AList;
FObject := TObject(FList.LockList.Items[AIndex]);
End;
//
// Реализация деструктора
Begin
FList.UnlockList;
Inherited;
End;
//
// Реализация метода GetObject
Begin
Result := FObject;
End;
//
Теперь можно реализовать и наш метод:
Function SafeGetObject(AIndex : Integer) : IObjectProvider;
Begin
Result := TObjectProvider.Create(ThreadSafeList, AIndex);
End;
Как видно из кода, метод возвращает не сам объект, а интерфейс объекта-провайдера, для обеспечения безопасности. Пользователь вызвав метод и раскрыв его оператором with, получает в эксклюзивное пользование затребованный объект, а после завершения работы с объектом сработает механизм подсчета ссылок и будет вызван деструктор объекта-провайдера, который в свою очередь разблокирует список, после чего объект-провайдер будет разрушен.
Пример:
With SafeGetObject(5) Do
Begin
Object.Method;
...
Object.Method;
End;
4 комментария:
А для какой задачи протребовалось это?
Это потребовалось для поддержки перечисления циклом for-in-do задач находящихся в пуле потоков. По подобной схеме был реализован перечислитель блокирующий список перед перечислением и снимающий блокировку после.
Я бы назвал не Provider а Holder )
Было бы интересно если бы Provider создавался на стеке.
>Я бы назвал не Provider а Holder
По смыслу это именно провайдер.
>Было бы интересно если бы Provider создавался на стеке
Со стеком будут проблемы, а вот избавиться от создания объекта-провайдера в общем-то можно. И даже, как мне видится, двумя способами.
Отправить комментарий