Correct way to implement a resource pool

后端 未结 2 1669
走了就别回头了
走了就别回头了 2021-01-16 22:49

I\'m trying to implement something that manages a pool of resources such that the calling code can request an object and will be given one from the pool if it\'s available,

2条回答
  •  轮回少年
    2021-01-16 23:12

    As was already mentioned in the comments, a counting semaphore is your friend. Combine this with a concurrent stack and you have got a nice simple, thread safe implementation, where you can still lazily allocate your pool items.

    The bare-bones implementation below provides an example of this approach. Note that another advantage here is that you do not need to "contaminate" your pool items with an InUse member as a flag to track stuff.

    Note that as a micro-optimization, a stack is preferred over a queue in this case, because it will provide the most recently returned instance from the pool, that may still be in e.g. L1 cache.

    public class GenericConcurrentPool : IDisposable where T : class
    {
        private readonly SemaphoreSlim _sem;
        private readonly ConcurrentStack _itemsStack;
        private readonly Action _onDisposeItem;
        private readonly Func _factory;
    
        public GenericConcurrentPool(int capacity, Func factory, Action onDisposeItem = null)
        {
            _itemsStack = new ConcurrentStack(new T[capacity]);
            _factory = factory;
            _onDisposeItem = onDisposeItem;
            _sem = new SemaphoreSlim(capacity);
        }
    
        public async Task CheckOutAsync()
        {
            await _sem.WaitAsync();
            return Pop();
        }
    
        public T CheckOut()
        {
            _sem.Wait();
            return Pop();
        }
    
        public void CheckIn(T item)
        {
            Push(item);
            _sem.Release();
        }
    
        public void Dispose()
        {
            _sem.Dispose();
            if (_onDisposeItem != null)
            {
                T item;
                while (_itemsStack.TryPop(out item))
                {
                    if (item != null)
                        _onDisposeItem(item);
                }
            }
        }
    
        private T Pop()
        {
            T item;
            var result = _itemsStack.TryPop(out item);
            Debug.Assert(result);
            return item ?? _factory();
        }
    
        private void Push(T item)
        {
            Debug.Assert(item != null);
            _itemsStack.Push(item);
        }
    }
    

提交回复
热议问题