Creating object pool with ConcurrentStack

An object pool is a set of pre-initialized objects that your application can use, rather than creating and destroying all of the objects it needs. If the instantiation cost of an object type is high, your application might benefit from a pool of objects.

In this recipe, we are going to create an object pool based on ConcurrentStack. ConcurrentStack will handle concurrent access issues using fast interlocked operations, and will dispense our objects in a LIFO manner. We will also have an object pool client which creates three tasks. One creates objects and puts them in the pool, the other two tasks request objects from the pool on different threads.

How to do it…

Let's see how we can use ConcurrentStack to build a pool of pre-initialized objects.

  1. Start a new project using the C# Console Application project template and assign ObjectPool as the Solution name.
  2. Let's start by creating our object pool class. Right-click on the ObjectPool project in the Solution Explorer and click on Add, then choose New Item. Select Visual C# Items, and Class. Enter ConcurrentObjectPool as the name of the class.
  3. Add the following using directives to the top of your ConcurrentObjectPool class:
    using System;
    using System.Collections.Concurrent;
  4. We want our object pool to work with any type, so add a generic type parameter after the class name.
    public class ConcurrentObjectPool<T>
    {
    }
  5. Our ObjectPool class is going to need a couple of private state fields. We need a ConcurrentStack field which will provide our backing store and a Func<T> field which will hold an object creation function the pool can use to generate objects when the pool is empty. Inside the class declaration, add the following fields:
    private ConcurrentStack<T> _objects;
    private Func<T> _objectInitializer;
  6. Now we need a constructor for the ConcurrentObjectPool class. The constructor should take a Func<T> argument for the object generator and should instantiate a new ConcurrentStack object as the backing store.
    public ConcurrentObjectPool(Func<T> objectInitializer)
    {
      _objects = new ConcurrentStack<T>();
      _objectInitializer = objectInitializer;
    }
  7. Now we need a GetObject method which will return a new object to the client. The GetObject method will try to pop an object off the stack. If it can't pop one off the stack, it will use objectInitializer to instantiate a new object.
    public T GetObject()
    {
      T item;
      if (_objects.TryPop(out item)) return item;
      return _objectInitializer();
    }
  8. The last step for our object pool is a PutObject method that takes a generic item parameter and pushes it on the stack.
    public void PutObject(T item)
    {
      _objects.Push(item);
    }
  9. Now we need to create the Console application that will use the object pool. Go back to Program.cs and add the following using directives at the top of the file:
    using System;
    using System.Text;
    using System.Threading.Tasks;
  10. The first step is to instantiate our object pool. In the main method of the program class, create a ConcurrentObjectPool object and pass in a function that creates a new StringBuilder object as the constructor parameter.
    var pool = new ConcurrentObjectPool<StringBuilder>(()=>
      new StringBuilder("Pooled Object created by objectInitializer"));
  11. Now let's create a task that creates some objects and places them in pool using the PutObject method.
    var task1 = Task.Factory.StartNew(() =>
    {
      for (var index = 0; index < 10; index++)
      {
        StringBuilder newObject = new StringBuilder(string.Concat("Pooled object", 
              index.ToString()));
        Console.WriteLine("Putting pooled object: {0}", index.ToString());
        pool.PutObject(newObject);
      }
    });
  12. Finally, let's create two continuation tasks that run after the first task is completed. Both tasks just request objects from the object pool using the GetObject method.
    task1.ContinueWith((antecedent)=>
    {
      for (var index = 0; index < 10; index++)
      {
        var pooledObject = pool.GetObject();
        Console.WriteLine("First Task: {0}", pooledObject.ToString());                    
      }
    });
    
    task1.ContinueWith((antecedent) =>
    {
      for (var index = 0; index < 10; index++)
      {
        var pooledObject = pool.GetObject();
        Console.WriteLine("Second Tasks: {0}", pooledObject.ToString());
      }
    });
  13. In Visual Studio 2012, press F5 to run the project. You should see the output as shown in the following screenshot:
    How to do it…

How it works…

There are other features we could have added to our object pool, such as controlling the concurrency level and/or using thread local segments to store our objects, but this simple implementation does the job for our purposes.

The constructor of the object pool takes a function argument that it can use to generate an object if the pool is empty, and stores the function in a private objectInitializer field. We are just pooling StringBuilder objects in this sample, so we passed in the following function:

 ()=>new StringBuilder("Pooled Object created by objectInitializer")

Our GetObject method, which the client uses to get objects from pool, just uses the TryPop method of ConcurrentStack to return an object. If TryPop fails to return anything because the stack is empty, we just return the result of the objectInitializer function.

public T GetObject()
{
  T item;
  if (_objects.TryPop(out item)) return item;
  return _objectInitializer();
}

The PutObject method probably doesn't require much explanation. It just uses the Push method of ConcurrentStack to push an object onto the stack.

Given that we chose to use ConcurrentStack, our object references are returned in a LIFO fashion. We could have chosen another type of backing store if this didn't work for us. For example, we could have chosen to use ConcurrentQueue as a backing store to have items returned in a First-In-First-Out (FIFO) fashion, or we could have used ConcurrentBag to provide unordered storage.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.233.62