Универсальный пул

habr.png
У нас есть 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.

Проект

© Habrahabr.ru