Click here to Skip to main content
15,881,709 members
Articles / Programming Languages / C#

Object Pool Class for C#/.NET Applications

Rate me:
Please Sign up or sign in to vote.
4.71/5 (8 votes)
28 Apr 2011CPOL3 min read 48.8K   757   27   13
This article is about a thread safe object pool class that is very easy to use.

Introduction

This article is about an easy to use thread safe object pool class. All you need is to implement an interface IPoolable and your class is good for object pooling :). A code snippet in the Using the Code section below shows how easy it is to pool your objects.

Background

While working on a gaming platform, I needed to show multiple gaming forms as quickly as possible. Forms creation time was becoming a bottle neck as it contained lots of controls and docking windows. To save creation time and to reuse gaming forms (instead of closing them and letting the garbage collector delete them from memory), I used object pooling.

Whenever an end user opens a gaming form, I take it from an object pool (instead of creating a new one every time). Similarly, when the end user closes the gaming forms, I simply make them invisible and take them back to the object pool.

C#
// Obtain objects from pool
SampleForm x = ObjectPool.New<sampleform>();

// return objects to object pool
ObjectPool.Delete<sampleform>(x);

// again obtain objects from object pool, note that
// objects will be reused
SampleForm x2 = ObjectPool.New<sampleform>();

Another reason for having an object pool is to bring scalability in our gaming server by reusing server objects through none other than object pooling. We get hundreds of thousands of requests from gaming clients every minute. If we serve objects like packets and socket info is created with every request, then the garbage collector could melt down the server and our server would not scale.

Using the code

Using the ObjectPool class is pretty straightforward. All you need is to implement an interface IPoolable in your class:

C#
public class SampleClass : IPoolable
{
    public void Create()
    {
    }

    public void New()
    {
    }

    public void Delete()
    {
    }
}
  • Create method is straightforward and it is there to do time the costly operation of object creation. Note that your class must have a default constructor to participate in object pooling. In the Create method, write the time costly logic of creating child controls, obtaining various things from database, etc. This method is called once right after the default constructor from the ObjectPool class.
  • New method is used to initialize your object. It is called whenever an object is served from the ObjectPool class. Use this method to reset your object to its initial state. E.g., adding events, initializing variables. Keep the code to a minimum in New. It should return as soon as possible to keep it efficient. At times, you will keep this method empty and put all your un-initialization logic in Delete, which is called when you return the object to the object pool.
  • Delete method is used to un-initialize your object. E.g., removing events, resetting class members to initial state, etc. This method is called whenever you return your object to the object pool.
C#
public void Test()
{
    // Obtain objects from pool
    SampleForm x = ObjectPool.New<sampleform>();
    SampleForm x1 = ObjectPool.New<sampleform>();
    SampleForm x2 = ObjectPool.New<sampleform>();
    SampleClass x3 = ObjectPool.New<sampleclass>();

    // return objects to object pool
    ObjectPool.Delete<sampleform>(x);
    ObjectPool.Delete<sampleform>(x1);
    ObjectPool.Delete<sampleform>(x2);
    ObjectPool.Delete<sampleclass>(x3);

    // again obtain objects from object pool, note that
    // objects will be reused
    SampleForm x4 = ObjectPool.New<sampleform>();
    SampleClass x5 = ObjectPool.New<sampleclass>();
}

Points of interest

I wanted to keep the API public interface simple, clean, and type safe, and it took some time to reach the current form. Deciding between interface and Generics with the right constraints was kind of tricky, mostly due to the where syntax :). Besides that, making the class thread safe and using Dictionary/Stack as data structures seemed a bit of trade off between performance and ease of use. Here is the full source code. Please do let me know of any bugs or any other improvements that you think are possible.

C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace App.Model
{
    #region Example Usage
    namespace ObjectPoolTester
    {
        #region ObjectPoolTester
        public class ObjectPoolTester
        {
            public void Test()
            {
                // Obtain objects from pool
                SampleForm x = ObjectPool.New<sampleform>();
                SampleForm x1 = ObjectPool.New<sampleform>();
                SampleForm x2 = ObjectPool.New<sampleform>();
                SampleClass x3 = ObjectPool.New<sampleclass>();

                // return objects to object pool
                ObjectPool.Delete<sampleform>(x);
                ObjectPool.Delete<sampleform>(x1);
                ObjectPool.Delete<sampleform>(x2);
                ObjectPool.Delete<sampleclass>(x3);

                // again obtain objects from object pool, note that
                // objects will be reused
                SampleForm x4 = ObjectPool.New<sampleform>();
                SampleClass x5 = ObjectPool.New<sampleclass>();
            }
        }
        #endregion

        #region SampleClass
        public class SampleClass : IPoolable
        {
            public void Create()
            {
            }

            public void New()
            {

            }

            public void Delete()
            {

            }
        }
        #endregion

        #region SampleForm
        public class SampleForm : IPoolable
        {
            public void Create()
            {
            }

            public void New()
            {

            }

            public void Delete()
            {

            }
        }
        #endregion
    }
    #endregion

    #region IPoolable
    public interface IPoolable
    {
        void Create();
        void New();
        void Delete();
    }
    #endregion

    #region ObjectPool
    public class ObjectPool
    {
        #region Data Members
        private static Dictionary<system.type,> pools = new Dictionary<type,>();

        #endregion

        #region New

        public static T New<t>() where T : IPoolable, new()
        {
            T x = default(T);

            if (pools.ContainsKey(typeof(T)))
            {
                x = (T)pools[typeof(T)].Pop();
            }
            else
            {
                lock (pools)
                {
                    pools[typeof(T)] = new PoolableObject(10);
                }
            }

            if (x == null)
            {
                x = new T();
                x.Create();
            }
            x.New();

            return x;
        }

        #endregion

        #region Delete
        public static void Delete<t>(T obj) where T : IPoolable
        {
            if (pools.ContainsKey(typeof(T)))
            {
                obj.Delete();
                pools[typeof(T)].Push(obj);
            }
            else
            {
                throw new Exception("ObjectPool.Delete can not be 
                called for object which is not created using ObjectPool.New");
            }
        }

        #endregion

        #region Clear

        public static void Clear()
        {
            lock (pools)
            {
                foreach (PoolableObject po in pools.Values)
                {
                    po.Clear();
                }

                pools.Clear();
            }
        }
        #endregion
    }
    #endregion

    #region PoolableObject
    public class PoolableObject
    {
        #region Data Members
        private Stack<ipoolable> pool;

        #endregion

        #region Ctor
        public PoolableObject(int capacity)
        {
            pool = new Stack<ipoolable>(capacity);
        }
        #endregion

        #region Properties
        public Int32 Count
        {
            get { return pool.Count; }
        }
        #endregion

        #region Pop
        public IPoolable Pop()
        {
            lock (pool)
            {
                if (pool.Count > 0)
                {
                    return pool.Pop();
                }

                return null;
            }
        }
        #endregion

        #region Push
        public void Push(IPoolable obj)
        {
            if (obj == null)
            {
                throw new ArgumentNullException("Items added to a Pool cannot be null");
            }

            lock (pool)
            {
                pool.Push(obj);
            }
        }
        #endregion

        #region Clear
        public void Clear()
        {
            lock (pool)
            {
                pool.Clear();
            }
        }
        #endregion
    }
    #endregion
}

History

First version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Pakistan Pakistan
Software Developer from Karachi, Pakistan.

Comments and Discussions

 
Generalnice article Pin
Santosh K. Tripathi7-Apr-15 15:43
professionalSantosh K. Tripathi7-Apr-15 15:43 
QuestionPooled Object Garbage Collector Pin
jaimebula23-Jun-11 15:53
jaimebula23-Jun-11 15:53 
AnswerRe: Pooled Object Garbage Collector Pin
Syed Rafey Husain24-Jun-11 1:51
Syed Rafey Husain24-Jun-11 1:51 
GeneralRe: Pooled Object Garbage Collector Pin
jaimebula24-Jun-11 3:57
jaimebula24-Jun-11 3:57 
GeneralRe: Pooled Object Garbage Collector Pin
Syed Rafey Husain24-Jun-11 21:58
Syed Rafey Husain24-Jun-11 21:58 
GeneralGood Stuff!!! Pin
depakb28-Apr-11 17:45
depakb28-Apr-11 17:45 
GeneralRe: Good Stuff!!! Pin
Syed Rafey Husain28-Apr-11 19:11
Syed Rafey Husain28-Apr-11 19:11 
This is so cool Smile | :) We are doing our gaming platform for around 2 years in .NET 3.5. So unfortunately, I can not use ConcurrentStack<t>. But thanks for pointing it out.
Regards and nice day,
Rafey, Husain.

GeneralObjectPool and "Thread safe", comments Pin
kornman0028-Apr-11 7:08
kornman0028-Apr-11 7:08 
GeneralRe: ObjectPool and "Thread safe", comments Pin
Syed Rafey Husain28-Apr-11 19:08
Syed Rafey Husain28-Apr-11 19:08 
GeneralRe: ObjectPool and "Thread safe", comments Pin
kornman0028-Apr-11 20:22
kornman0028-Apr-11 20:22 
GeneralRe: ObjectPool and "Thread safe", comments Pin
Syed Rafey Husain28-Apr-11 22:04
Syed Rafey Husain28-Apr-11 22:04 
GeneralRe: ObjectPool and "Thread safe", comments Pin
supercat929-Apr-11 5:07
supercat929-Apr-11 5:07 
GeneralRe: ObjectPool and "Thread safe", comments Pin
Syed Rafey Husain1-May-11 18:45
Syed Rafey Husain1-May-11 18:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.