Object pooling is a design pattern that can be used when the instantiation of objects is an expensive operation, or when memory allocation and garbage collection can be issues. An object pool keeps a set of objects alive ready for use for the program so that these objects don’t need to be created every time they are needed.

Instead, the program can ask for one of these objects at runtime, use it, and then return it to the object pool. Later, the same object will be available again, ready to be used.

More than one thread

It’s all well and good when only one thread has access to an object pool, but what happens when more than one thread need to access a pool to get an object quickly? Race conditions could occur when both threads access the pool at the same time and received the same object back. At this point both threads are using the same object and Bad Things™ will happen!

This is because the pool is not thread safe and it doesn’t prevent its access to only one thread at a time. So how to solve this?

Use a Lock

A straightforward way to solve this issue is to use a lock statement which will literally lock the access to the pool and force any other thread who are not in possession of such lock, to wait for its release before resuming execution.

        private readonly object fooLock = new object();

        void GetPooledObject()
        {
            lock( fooLock )
            {
                // Grab pooled object and return it
            }
        }

This will lock everything wrapped around the lock scope, but it is not as efficient as it could be: each thread will have to wait for the previous one to release the lock before being able to proceed. Also if thread-fairness is something of importance to you, probably using a lock is not the best decision.

Luckily for us, the .Net framework provides us with data structures that are already thread-safe!

Concurrent Collections

The .NET Framework 4 comes with a whole lot of collections which are concurrent and thread safe. This means that they can be accessed by multiple threads at the same time.

Turns-out that most of these collections are also lock-free, and this should give you better performance too.

If you want to know more about these collection, go check the links below and have a poke around the System.Collections.Concurrent namespace.

ConcurrentPool.cs

    public class ConcurrentPool<T> where T : IResettable
    {
        // -----------------------------------------------------------------
        // Data
        // -----------------------------------------------------------------
        ConcurrentBag<PooledObject<T>> mPool = new ConcurrentBag<PooledObject<T>>();
        Func<T> mFactory;

        // -----------------------------------------------------------------
        // Constructors
        // -----------------------------------------------------------------
        public ConcurrentPool( Func<T> inFactory )
        {
            mFactory = inFactory ?? throw new Exception( "[Concurrent Pool] Factory Function is null" );
        }

        // -----------------------------------------------------------------
        // Functions
        // -----------------------------------------------------------------
        public PooledObject<T> Get()
        {
            if( mPool.TryTake( out PooledObject<T> returnObject ) )
            {
                return returnObject;
            }

            T newObject = mFactory();
            PooledObject<T> pooledObject = new PooledObject<T>( newObject, this );

            return pooledObject;
        }

        // -----------------------------------------------------------------
        public void Return( PooledObject<T> inToReturn )
        {
            if( inToReturn != null )
            {
                mPool.Add( inToReturn );
            }
        }
    }

Concurrent pool is a template class, which means it can be used with different types, but these types need to implement the IResettable interface. I defined the IResettable interface so that it is possible to call Reset() on the object that is returned.

You might have noticed that the pool doesn’t store objects of type <T> directly, but actually uses a wrapper class PooledObject<T>.

PooledObject.cs

    public class PooledObject<T> : IDisposable where T : IResettable
    {
        // -----------------------------------------------------------------
        // Data
        // -----------------------------------------------------------------
        private ConcurrentPool<T> mConcurrentPool;
        private readonly T mPooledObject;

        // -----------------------------------------------------------------
        // Constructor
        // -----------------------------------------------------------------
        public PooledObject( T inOnbject, ConcurrentPool<T> inConcurrentPool )
        {
            mPooledObject = inOnbject;
            mConcurrentPool = inConcurrentPool ?? throw new Exception("[Pooled Object] Concurrent Pool is null");
        }

        // -----------------------------------------------------------------
        // IDisposable Interface
        // -----------------------------------------------------------------
        public void Dispose()
        {
            mPooledObject.Reset();
            mConcurrentPool.Return( this );   
        }

        // -----------------------------------------------------------------
        public T Value
        {
            get
            {
                return mPooledObject;
            }
        }
    }

The reason to use PooledObject is because I want to wrap T objects around an IDisposable object so that they can be automatically reset and returned to the pool when the object is disposed. By doing it this way, I won’t have to worry about calling ConcurrentPool.Return explicitly after I’m done using the object. It will also result in very clean code since it is possible to take advantage of the using statement – which expects the IDisposable interface – which in turn disposes of the object as soon as the execution goes out of its scope.

Usage

            ConcurrentPool<DummyClass> pool = new ConcurrentPool<DummyClass>( () => new DummyClass() );

            Parallel.For( 0, 10000, ( index ) =>
            {
                using( PooledObject<DummyClass> DummyClass = pool.Get() )
                {
                    ProcessDummy( DummyClass.Value );
                }
            }
            );

Conclusions

We have looked at what an Object pool is and what its advantages are. We also discussed what happens when multi-threading is introduced and how we can solve race conditions using a lock or concurrent collections. We also looked at how to use the IDisposable interface to return pooled objects automatically.

An intersting exercises could be the use of all the different concurrent collections available and check for any perfomance differences.

Find my on my Github here: https://github.com/lormori/design-patterns

References

https://devblogs.microsoft.com/pfxteam/faq-are-all-of-the-new-concurrent-collections-lock-free/
https://stackoverflow.com/questions/4228864/does-lock-guarantee-acquired-in-order-requested