
У нас есть ArrayPool для переиспользования массивов. Работает это так: взяли массив определенной длинны что то с ним поделали и положили обратно. Нужно это для больших объектов которые по логике программы долго не должны храниться. В предыдущей статье описана эта проблема.
Так же могут понадобиться не только массивы, поэтому попробуем написать универсальный пул.
Нам понадобится хранилище для элементов ConcurrentBag и методы для создания и очистки элемента в хранилище.
public sealed class Pool<T>
{
private ConcurrentBag<T> _items =
new ConcurrentBag<T>();
private readonly Func<T> _itemCreator = null;
private readonly Action<T> _itemClearer = null;
public Pool(Func<T> itemCreator)
{
_itemCreator = itemCreator ?? throw new ArgumentNullException("itemCreator");
}
public Pool(Func<T> itemCreator, Action<T> itemClearer) : this(itemCreator)
{
_itemClearer = itemClearer ?? throw new ArgumentNullException("itemClearer");
}
public T Rent()
{
if (_items.TryTake(out var item))
return item;
return _itemCreator();
}
public void Return(T item)
{
_itemClearer?.Invoke(item);
_items.Add(item);
}
public int Count()
{
return _items.Count;
}
}
Метод Rent берет элемент или создает, а метод Return возвращает в ConcurrentBag, перед этим очистив его если это требуется.
Конструктор с одним параметром означает что элементы не будут очищаться.
Приведу пример для List:
var bigListPool = new Pool<List<long>>(Creator, Clearer);
List<long> list = null;
try
{
list = bigListPool.Rent();
//тут работа с list
}
finally
{
if (list != null)
bigListPool.Return(list);
}
Создание и очистка List:
List<long> Creator() => new List<long>(1024 * 1024);
void Clearer(List<long> l) => l.ForEach(i => i = 0);
Если список другого размера то надо создать еще один пул. Далее можно все это дело обернуть и внедрить синглтоном через ConfigureServices.
Комментариев нет:
Отправить комментарий