BlackWaspTM

This web site uses cookies. By using the site you accept the cookie policy.This message is for compliance with the UK ICO law.

Design Patterns
.NET 3.5+

A Generic Object Pool

The object pool design pattern can improve performance by reusing objects that are expensive to instantiate, rather than creating new instances. This article describes a generic implementation of the object pool pattern.

Limiting the Pool Size

The last element to include in our generic object pool is a limitation to the size of the pool. We need to be able to set a maximum number of objects and to throw an exception if an attempt is made to exceed this limit.

To begin, let's add two properties. The first holds the maximum number of items in the pool. This is read-only to ensure that it cannot be modified once a pool is instantiated. The second returns the number of items that are currently in the pool. This is calculated by adding the number of items that are in use to the number that are available.

public int MaxPoolSize { get; private set; }

public int PoolSize
{
    get { return _available.Count + _inUse.Count; }
}

We'll set the maximum pool size during instantiation. Modify the constructor as shown below to add a parameter that receives the limit. The final line of code stores the provided value in the property:

public Pool(Action<T> cleanUp, int maxPoolSize)
{
    if (cleanUp == null) throw new ArgumentNullException("cleanUp");

    _cleanUp = cleanUp;
    MaxPoolSize = maxPoolSize;
}

Before we add the check, let's refactor the Get method to ensure it remains easy to read and maintain. Extract the contents of the else block into a new method named, "AddToPool". The two methods should appear as shown below:

public T Get()
{
    lock (_available)
    {
        if (_available.Count != 0)
        {
            T obj = _available[0];
            _inUse.Add(obj);
            _available.RemoveAt(0);
            return obj;
        }
        else
        {
            return AddToPool();
        }
    }
}

private T AddToPool()
{
    T obj = new T();
    _inUse.Add(obj);
    return obj;
}

We can now add the size check before adding a new item to the pool. If the pool is already at its maximum size, we'll throw an exception:

private T AddToPool()
{
    if (PoolSize == MaxPoolSize)
    {
        throw new InvalidOperationException("Pool exhausted");
    }
	
    T obj = new T();
    _inUse.Add(obj);
    return obj;
}

To demonstrate the new size limit, let's first ensure that the test code operates correctly when the pool is sufficiently large. Modify the Main method's contents as shown below to work with a pool that can supply three objects. Run the program to see that the objects are created and returned as expected.

var pool = new Pool<PooledObject>(p => { p.Name = null; }, 3);

var obj1 = pool.Get();
obj1.Name = "First";
Show(obj1);

var obj2 = pool.Get();
obj2.Name = "Second";
Show(obj2);

var obj3 = pool.Get();
obj3.Name = "Third";
Show(obj3);

/* OUTPUT

1 - First
2 - Second
3 - Third

*/

Next, modify the creation of the pool to restrict it to only two objects.

var pool = new Pool<PooledObject>(p => { p.Name = null; }, 2);

Try running the program for a final time to see the exception when the queue is exhausted.

23 July 2013