Home > Threading > Generic ConcurrentPool

Generic ConcurrentPool


This situation is fairly common: you need to re-use the same class over and over and you wish you didn’t have to pay the price for multiple allocations and garbage collection. The .NET framework doesn’t come with a generic pool class unfortunately so you have to create your own. I’ve seen very complicated implementations as well as others that weren’t thread-safe.

The following generic implementation relies on the .NET 4.0 ConcurrentBag<T> for storage which is thread-safe. The capacity parameter specifies the initial capacity of the pool.

public class ConcurrentPool<T> where T : class
{
    private ConcurrentBag<T> _bag = new ConcurrentBag<T>();
    private Func<T> _factoryMethod;

    public ConcurrentPool(int capacity)
    {
        if (capacity < 0)
            throw new ArgumentOutOfRangeException("capacity");

        if (typeof(T).GetConstructor(Type.EmptyTypes) == null)
            throw new ArgumentException(string.Format("Type '{0}' " +
                "doesn't have a parameterless constructor. " +
                "Specify a factoryMethod.", typeof(T).Name));

        _factoryMethod = () => Activator.CreateInstance<T>();
        Initialize(capacity);
    }
    public ConcurrentPool(int capacity, Func<T> factoryMethod)
    {
        if (capacity < 0)
            throw new ArgumentOutOfRangeException("capacity");

        if (factoryMethod == null)
            throw new ArgumentNullException("factoryMethod");

        _factoryMethod = factoryMethod;
        Initialize(capacity);
    }

    private void Initialize(int capacity)
    {
        for (int i = 0; i < capacity; i++)
        {
            _bag.Add(_factoryMethod());
        }
    }

    public virtual T Take()
    {
        T item;
        return _bag.TryTake(out item) ? item : _factoryMethod();
    }

    public virtual void Add(T item)
    {
        _bag.Add(item);
    }
}

As more objects are requested, new instances will be created. Note that the Take and Add methods are virtual if you need to perform some specific logic when taking or re-adding an object to the pool. You can also implement the IProducerConsumerCollection<T> interface in the ConcurrentPool if this is something you need (I’ve omitted it to keep this article short).

Any other IProducerConsumerCollection could be used as the underlying collection (ConcurrentQueue or ConcurrentStack for example). I’ve used the ConcurrentBag here because it provides the best performance out of those 3 for retrieval from what I’ve measured.

Advertisements
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: