tags:

views:

343

answers:

5

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.

+1  A: 

How about System.Collections.Generic.Dictionary?

Boris Lipschitz
While a dictionary might be used in an object pool implementation, it doesn't offer any of the features (such as a strategy for satisfying requests when the pool is empty), except storage.
Jeff Sternal
It's usually, straight forward to add a "Factory" support as well as multithreading. The Pool might preload the objects on start (e.g from database) or create them on fly when requested. The full design of such class depends on your requirements.
Boris Lipschitz
+2  A: 

CodeProject has a sample ObjectPool implementation. Have a look here. Alternatively, there are some implementations here, here, and here.

Damovisa
A: 

Sounds like you need a Factory pattern with caching.

You can try use .net reflector to look at the ThreadPool implementation.

vanja.
+4  A: 

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.

spender
Why exactly would you use a `HashSet`?
Dan Tao
y'see, that code is now released under a CC-Wiki license so i can't use it without releasing *my* code under that license, and i'm not sure that i want to do that...
RCIX
I don't understand how the proposed solution is related to the question. It seems like a fancy Queue rather than a ObjectsPool.
Boris Lipschitz
You could use it as inspiration for your ObjectPool, rather than directly copy it.
Alastair Pitts
When I see code like this, I just kind of "drool" thinking about how much I don't understand it, and would like to understand it :) And, one day, perhaps, I will. Yes, I voted the message up. thanks,
BillW
@RCIX: didn't think much about the implications of posting here. Um. Attribution would be good, but other conditions waived.@Dan: HashSet was used to prevent duplicates finding their way into the Pool. Of course it could be backed off by some other collection, but I didn't want to risk handing off buffers to 2 clients at the same time.@Boris: It is indeed a "fancy queue", but it's been really handy for things like short-lived buffers that would need creating at the order of thousands a second, when effectively only about 100 are in use at any one time. The alternative makes GC grind a little
spender
Oh cool, thanks! :D
RCIX
@RCIX: I am not a lawyer and everything, blah blah blah... but wouldn't the cc-wiki license be satisfied if: A) You put the Object Pool code into it's own DLL and then linked to that, B) posted your revisions back here (create your own answer to describe what you're doing and the resulting code...) and C) gave attribution to the author in your final project by linking to his user page and to the license text?
jasonh
+3  A: 

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).

Dan Tao