📜 ⬆️ ⬇️

Universal pool

We have an ArrayPool to reuse arrays. It works like this: they took an array of a certain length and did something with it and put it back. It is necessary for large objects that, according to the logic of the program, should not be stored for a long time. The previous article described this problem.

Not only arrays may also be needed, so let's try to write a universal pool. I will say in advance that there are ready-made solutions .

We will need a repository and methods for creating and cleaning an item in the repository.
')
public sealed class Pool<T> { readonly List<T> _items = new List<T>(); readonly Func<T> _itemCreator = null; readonly Action<T> _itemClearer = null; readonly int _maxItems; SpinLock _lock; public Pool(Func<T> itemCreator, int maxItems) { _itemCreator = itemCreator ?? throw new ArgumentNullException(nameof(itemCreator)); _maxItems = maxItems; } public Pool(Func<T> itemCreator, Action<T> itemClearer, int maxItems) : this(itemCreator, maxItems) { _itemClearer = itemClearer ?? throw new ArgumentNullException(nameof(itemClearer)); } public Pooled Rent() { T rented; bool lockTaken = false; try { _lock.Enter(ref lockTaken); if (_items.Any()) { rented = _items[0]; _items.RemoveAt(0); } else rented = _itemCreator(); } finally { if (lockTaken) _lock.Exit(); } return new Pooled(rented, this); } public int Count => _items.Count; public class Pooled : IDisposable { readonly Pool<T> _pool; public readonly T item; public Pooled(T item, Pool<T> pool) { this.item = item; _pool = pool; } bool disposedValue = false; public void Dispose() { bool lockTaken = false; try { _pool._lock.Enter(ref lockTaken); var dValue = Volatile.Read(ref disposedValue); if (!dValue) { Volatile.Write(ref disposedValue, true); if (_pool.Count < _pool._maxItems) { _pool._itemClearer?.Invoke(item); _pool._items.Add(item); } } } finally { if (lockTaken) _pool._lock.Exit(); } } } } 


The Rent method takes the item wrapped in Pooled or creates it, and the return will occur in Dispose.

A constructor with one parameter means that the elements will not be cleared.

I will give an example for the List:
  var bigListPool = new Pool<List<long>>(Creator, Clearer, 10); using (var pooledList = bigListPool.Rent()) { var list = pooledList.item; } 


Creating and cleaning the List:
  List<long> Creator() => new List<long>(1024 * 1024); void Clearer(List<long> l) => l.ForEach(i => i = 0); 


If the list is of a different size, then you need to create another pool. Then you can wrap this whole thing and implement it via ConfigureServices.

Project

Source: https://habr.com/ru/post/452694/


All Articles