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.

Object Pool Design Pattern

In a recent article I described the Object Pool design pattern. This creational pattern favours reuse of existing objects over creation of new instances, which can improve performance if the types involved are expensive or slow to generate. The pattern uses an expandable pool of objects that can be cleaned up and reused as required.

In this article we'll look at a generic class that implements the design pattern. The class will provide the following benefits:

  • The pool will be generic, allowing almost any type of object to be pooled. NB: In the case of the example code shown in the text, and available to download using the link at the start of the article, there is a limitation. Only classes with a default constructor can be pooled.
  • Objects will be instantiated automatically if the pool is currently empty or all previously created objects are in use.
  • It will be possible to create multiple pools, with some pools potentially holding the same type of object as others.
  • There will be a maximum number of objects per pool. This maximum will be configurable for each pool individually. When a pool is exhausted and a new object is requested, an exception will be thrown. NB: This functionality could be modified to create a pool that blocks the thread until an object becomes available.
  • The clean-up process that occurs when an object is released back to the pool, and before it is reused, will be configurable. When two pools that contain the same type of object are created, it will be possible to use different clean-up code for each pool.

Creating the Project

To begin, we need a new project. Start Visual Studio and create a console application project. We'll use the Main method later to demonstrate the class.

Adding the Pool<T> Class

Let's start with the Pool class. This is the generic type that represents pools of objects. Add a new class to the project, naming the file, "Pool.cs". Replace the automatically added class code with that shown below:

public class Pool<T> where T : new()
{
    List<T> _available = new List<T>();
    List<T> _inUse = new List<T>();

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

    public void Release(T obj)
    {
        CleanUp(obj);

        lock (_available)
        {
            _available.Add(obj);
            _inUse.Remove(obj);
        }
    }

    private void CleanUp(T obj)
    {
    }
}

If you compare this code with that of the "Object Pool Design Pattern" article you can see that the two samples are roughly the same. The key difference is the addition of the generic type parameter to allow a variety of types to be pooled. A generic constraint is included to ensure that the pooled type includes a default constructor. This is necessary so that new items can be added to the pool by the Get method.

The second change is to the CleanUp method. As we do not know what is required to reset objects that could be of many different types, this method is currently empty. We could make the method call a member of the pooled object itself. However, this would add an extra restriction to pooled types. We would have to ensure that they implemented a specific interface or inherited from a particular base class. If we wanted to pool sealed types from the .NET framework, this restriction would be a problem. We'll complete the configurable clean-up process later in the article.

The final change is that the class is not static. This means we can create many pools, including several holding the same type of object.

To try the Pool class we need a type for it to hold. Add a new class named, "PooledObject", using the following code. This simple type has two properties. The Name property is a string that can be read or changed. PermanentId is an integer that is initialised in the constructor. Each new PooledObject will have a unique PermanentId that will remain even after release back to the pool.

public class PooledObject
{
    static object _idLock = new object();
    static int _nextId = 1;

    public PooledObject()
    {
        lock (_idLock)
        {
            PermanentId = _nextId++;
        }
    }

    public string Name { get; set; }

    public int PermanentId { get; private set; }
}

To create and use a new object pool, replace the contents of the Program class with that shown below. Here the Main method requests two objects from the pool and displays their properties. Next, one of the objects is released back to the pool. Finally, a new object is requested and displayed. We can see that a pooled object is reused because the PermanentId property is not unique in the third message.

static void Main(string[] args)
{
    var pool = new Pool<PooledObject>();

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

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

    pool.Release(obj1);

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

private static void Show(PooledObject obj)
{
    Console.WriteLine("{0} - {1}", obj.PermanentId, obj.Name);
}

/* OUTPUT

1 - First
2 - Second
1 - Third

*/
23 July 2013