Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ObjectPool<T> or similar for .NET already in a library?

Tags:

c#

.net

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 for .NET?

By object pool, i mean a class that assists caching of objects that take a long time to create, generally used to improve performance.

like image 363
RCIX Avatar asked Nov 09 '09 01:11

RCIX


People also ask

What is an object pool in .NET in C#?

ObjectPool is part of the ASP.NET Core infrastructure that supports keeping a group of objects in memory for reuse rather than allowing the objects to be garbage collected. You might want to use the object pool if the objects that are being managed are: Expensive to allocate/initialize.

What is the advantage of object pool design pattern?

Advantage of Object Pool design patternIt is most effective in a situation where the rate of initializing a class instance is high. It manages the connections and provides a way to reuse and share them. It can also provide the limit for the maximum number of objects that can be created.

Why are object pool used in c#?

Object pool is a software construct designed to optimize the usage of limited resources. It has objects that are ready to be used. On activation of the object, it is pulled from pool. On deactivation, the object is added to the pool.

What is an issue with the Object pool pattern?

In many applications of the Object Pool pattern, there are reasons for limiting the total number of Reusable objects that may exist. In such cases, the ReusablePool object that creates Reusable objects is responsible for not creating more than a specified maximum number of Reusable objects.


2 Answers

In the upcoming version of .NET (4.0), there's a ConcurrentBag<T> class which can easily be utilized in an ObjectPool<T> implementation; in fact the there's an article on MSDN that shows you how to do precisely this.

If you don't have access to the latest .NET framework, you can get the System.Collections.Concurrent namespace (which has ConcurrentBag<T>) in .NET 3.5 from Microsoft's Reactive Extensions (Rx) library (in System.Threading.dll).

like image 87
Dan Tao Avatar answered Oct 25 '22 22:10

Dan Tao


UPDATE:

I'd also put forward BufferBlock<T> from TPL DataFlow. IIRC it's part of .net now. The great thing about BufferBlock<T> is that you can wait asynchronously for items to become available using the Post<T> and ReceiveAsync<T> 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<T> where T : class
    {
        private readonly Queue<AsyncResult<T>> asyncQueue = new Queue<AsyncResult<T>>();
        private readonly Func<T> createFunction;
        private readonly HashSet<T> pool;
        private readonly Action<T> resetFunction;

        public Pool(Func<T> createFunction, Action<T> resetFunction, int poolCapacity)
        {
            this.createFunction = createFunction;
            this.resetFunction = resetFunction;
            pool = new HashSet<T>();
            CreatePoolItems(poolCapacity);
        }

        public Pool(Func<T> 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<T>();
            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<T>) 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<MemoryStream> msPool = new Pool<MemoryStream>(() => 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<TResult> implementation).

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

like image 27
spender Avatar answered Oct 25 '22 22:10

spender