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