ObjectPool or similar for .NET already in a library?

前端 未结 5 1962
余生分开走
余生分开走 2021-01-30 18:38

I don\'t want to write my own because i\'m afraid i might miss something and/or rip off other people\'s work, so is there an ObjectPool (or similar) class existing in a library

5条回答
  •  囚心锁ツ
    2021-01-30 19:17

    UPDATE:

    I'd also put forward BufferBlock from TPL DataFlow. IIRC it's part of .net now. The great thing about BufferBlock is that you can wait asynchronously for items to become available using the Post and ReceiveAsync extension methods. Pretty handy in an async/await world.

    ORIGINAL ANSWER

    A while back I faced this problem and came up with a lightweight (rough'n'ready) threadsafe (I hope) pool that has proved very useful, reusable and robust:

        public class Pool where T : class
        {
            private readonly Queue> asyncQueue = new Queue>();
            private readonly Func createFunction;
            private readonly HashSet pool;
            private readonly Action resetFunction;
    
            public Pool(Func createFunction, Action resetFunction, int poolCapacity)
            {
                this.createFunction = createFunction;
                this.resetFunction = resetFunction;
                pool = new HashSet();
                CreatePoolItems(poolCapacity);
            }
    
            public Pool(Func createFunction, int poolCapacity) : this(createFunction, null, poolCapacity)
            {
            }
    
            public int Count
            {
                get
                {
                    return pool.Count;
                }
            }
    
            private void CreatePoolItems(int numItems)
            {
                for (var i = 0; i < numItems; i++)
                {
                    var item = createFunction();
                    pool.Add(item);
                }
            }
    
            public void Push(T item)
            {
                if (item == null)
                {
                    Console.WriteLine("Push-ing null item. ERROR");
                    throw new ArgumentNullException();
                }
                if (resetFunction != null)
                {
                    resetFunction(item);
                }
                lock (asyncQueue)
                {
                    if (asyncQueue.Count > 0)
                    {
                        var result = asyncQueue.Dequeue();
                        result.SetAsCompletedAsync(item);
                        return;
                    }
                }
                lock (pool)
                {
                    pool.Add(item);
                }
            }
    
            public T Pop()
            {
                T item;
                lock (pool)
                {
                    if (pool.Count == 0)
                    {
                        return null;
                    }
                    item = pool.First();
                    pool.Remove(item);
                }
                return item;
            }
    
            public IAsyncResult BeginPop(AsyncCallback callback)
            {
                var result = new AsyncResult();
                result.AsyncCallback = callback;
                lock (pool)
                {
                    if (pool.Count == 0)
                    {
                        lock (asyncQueue)
                        {
                            asyncQueue.Enqueue(result);
                            return result;
                        }
                    }
                    var poppedItem = pool.First();
                    pool.Remove(poppedItem);
                    result.SetAsCompleted(poppedItem);
                    return result;
                }
            }
    
            public T EndPop(IAsyncResult asyncResult)
            {
                var result = (AsyncResult) asyncResult;
                return result.EndInvoke();
            }
        }
    

    In order to avoid any interface requirements of the pooled objects, both the creation and resetting of the objects is performed by user supplied delegates: i.e.

    Pool msPool = new Pool(() => new MemoryStream(2048), pms => {
            pms.Position = 0;
            pms.SetLength(0);
        }, 500);
    

    In the case that the pool is empty, the BeginPop/EndPop pair provide an APM (ish) means of retrieving the object asynchronously when one becomes available (using Jeff Richter's excellent AsyncResult implementation).

    I can't quite remember why it is constained to T : class... there's probably none.

提交回复
热议问题