Click here to Skip to main content
Click here to Skip to main content

Tagged as

Concurrent Object Pool, the Right Way

, 22 Apr 2013
Rate this:
Please Sign up or sign in to vote.
An implementation of a generic, concurrent object pool with smart memory management.

Introduction

This article presents an implementation of a generic, concurrent object pool. This one is not "yet another object pool" and was designed and implemented with the following properties in mind:

  1. Generic
  2. Thread-Safe
  3. No Memory Leaks

Additionally, this implementation presents a production-level code. It has been tested extensively and works well.

Background

"The object pool pattern is a software creational design pattern that uses a set of initialized objects kept ready to use, rather than allocating and destroying them on demand. A client of the pool will request an object from the pool and perform operations on the returned object. When the client has finished, it returns the object, which is a specific type of factory object, to the pool rather than destroying it. " -  Wikipedia.

In .NET, such collection has several advantages:

  • As the objects are never disposed, it minimizes garbage that a GC has to collect, therefore making a GC pause shorter.
  • It allows to increase the performance if you are allocating/destroying a massive amount of objects.
  • As the garbage is minimized, so is memory fragmentation.

Using the code 

Using the ConcurrentPool<T> is very easy. First of all, derive your object from RecyclableObject and implement the Recycle() method. Alternatively, you could implement the IRecyclable interface yourself. 

public class MyObject : RecyclableObject
{
    /// <summary>
    /// Some dummy property
    /// </summary>
    public string Name { get; set; }

    /// <summary>
    /// Resets the object state.
    /// </summary>
    public override void Recycle()
    {
        this.Name = String.Empty;    
    }
}

After that, you might want to instantiate an instance of ConcurrentPool<T>. This can be done in several ways, since the pool needs to be able to allocate the object itself. So, if you provide a default constructor on your recyclable object, you can instantiate a pool simply as:

var pool = new ConcurrentPool<MyObject>("Pool of MyObject"); 

If not, you can also provide a CreateInstanceDelegate to act as a constructor: 

var pool = new ConcurrentPool<MyObject>("Pool of MyObject", () => new MyObject() ); 

Finally, acquiring and releasing the objects is done via a disposable pattern.

using (MyObject instance = pool.Acquire())
{
     // Do something with the instance
}
// The instance is released back to the pool 

Alternatively, you could call TryRelease() to attempt to release the object back to the pool.

MyObject instance = pool.Acquire();
// Do something with the instance
instance.TryRelease();  

And that's it!

Points of Interest 

Now, let's have a look at how our ConcurrentPool<T> works.

In the beginning of the article, I mentioned that the presented implementation is generic, thread-safe, and ensures that no memory is leaked. While the first point is quite straightforward, the second is done via storing internally the items in a ConcurrentQueue<T>, from .NET Framework 4+.

We mentioned that RecyclableObject implements the IDisposable pattern, consider the following test:

static void Test1()
{
    Console.WriteLine();
    Console.WriteLine(" Test 1");
    Console.WriteLine(" ******");
    Console.WriteLine();

    // Acquire an instance from the pool
    using (A a1 = poolOfA.Acquire())
    {
        Diagnose("a1 acquired.", poolOfA);

        // Do something with the instance
    }

    // Here the object is disposed and released back to the pool.
    Diagnose("a1 disposed.", poolOfA);
}

static void Test2()
{
    Console.WriteLine();
    Console.WriteLine(" Test 2");
    Console.WriteLine(" ******");
    Console.WriteLine();

    // Attempt to leak memory by allocating two in a function and never releasing the objects
    // manually to the pool.
    AllocateMany();

    // We don't use them and we did not dispose them yet...
    Diagnose("function ended.", poolOfA);

    // Now, let's force GC to collect
    GC.Collect();
    GC.WaitForPendingFinalizers();
    GC.Collect();

    Thread.Sleep(100);

    // How many now?
    Diagnose("GC collected.", poolOfA);

}

static void AllocateMany()
{
    // Acquire more instances...
    for (int i = 0; i < 10; ++i)
    {
        A instance = poolOfA.Acquire();
    }
    Diagnose("10 instances acquired.", poolOfA);
}

In this snippet of code, Test2() attempts to leak memory by not disposing the objects manually. In that case (as we implement the IDisposable pattern), the objects are finalized by GC, and instead of being destroyed they are resurrected and are pushed back to the ConcurrentPool.

 > Pool created.
      Pool contains 0 items, 0 in use, 0 available.
 
 Test 1
 ******
 
 > a1 acquired.
      Pool contains 1 items, 1 in use, 0 available.
 > a1 disposed.
      Pool contains 1 items, 0 in use, 1 available.
 
 Test 2
 ******
 
 > 10 instances acquired.
      Pool contains 10 items, 10 in use, 0 available.
 > function ended.
      Pool contains 10 items, 10 in use, 0 available.
 > GC collected.
      Pool contains 10 items, 0 in use, 10 available. 

History    

  • 21 October 2012 - Initial release.

License

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

Share

About the Author

Kel_
Chief Technology Officer Misakai Ltd.
Ireland Ireland
Roman Atachiants is the guy behind www.spike-engine.com project, a real-time, client-server networking layer (SOA, RPC) for .NET developers. Also the founder of Misakai Ltd..
 
He is a software engineer and scientist with extensive experience in different computer science domains, programming languages/principles/patterns & frameworks.
 
His main expertise consists of C# and .NET platform, game technologies, cloud, human-computer interaction, big data and artificial intelligence. He has an extensive programming knowledge and R&D expertise.

 
Follow on   Twitter

Comments and Discussions

 
GeneralMy vote of 5 PinmemberOleksandr Kulchytskyi22-Apr-13 21:09 
GeneralMy vote of 4 PinprofessionalAlluvialDeposit22-Apr-13 11:08 
QuestionWhat if the object is Disposed and then used? PinmemberPaulo Zemek22-Apr-13 9:26 
AnswerRe: What if the object is Disposed and then used? PinmemberKel_22-Apr-13 10:24 
GeneralRe: What if the object is Disposed and then used? PinmemberPaulo Zemek22-Apr-13 10:26 
GeneralRe: What if the object is Disposed and then used? PinmemberKel_22-Apr-13 10:39 

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

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

| Advertise | Privacy | Mobile
Web02 | 2.8.140827.1 | Last Updated 22 Apr 2013
Article Copyright 2012 by Kel_
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid