Dec 18 2011

The Pre-emptively active Activator

In some high-performance scenario’s you may need to create objects at a higher rate than previous methods can support. This could also be true for scenario’s where the object creation processes involve a large number of steps before the object is considered fully constructed and ready for use. This can often be the case where surges of requests cause applications to breach Quality of Service (QoS) thresholds.

In cases such as this, a background object builder can help offset the expense of construction and provide the host some additional mitigation of its overall capacity during surges. Here is a sample implementation which will pre-build a queue of instances and will refresh its internal queue when the size falls below a configurable threshold.

public sealed class BackgroundActivator<T> : IBackgroundActivator<T>
    where T : class
{
    private readonly Func<T> _callback;
    private readonly int _refreshThreshold;
    private readonly ConcurrentQueue<T> _queue;
    private readonly Func<T> _constructor;
    private readonly int _size;
    private volatile bool _runningRefresh;

    public BackgroundActivator(int size, int refreshThreshold)
        : this(size, refreshThreshold, null)
    {
    }

    public BackgroundActivator(int size, int refreshThreshold, Func<T> callback)
    {
        if (size <= 0)
            throw new Exception("size <= 0");

        if (refreshThreshold >= size)
            throw new Exception("low >= size");

        if (callback == null)
            _constructor = GetConstructor();

        _size = size;
        _refreshThreshold = refreshThreshold;

        _callback = callback;
        _queue = new ConcurrentQueue<T>();

        for (int i = 0; i < size; ++i)
        {
            T t = CreateInstanceInternal();

            _queue.Enqueue(t);
        }
    }

    private static Func<T> GetConstructor()
    {
        ConstructorInfo constructorInfo = typeof(T).GetConstructor(new Type[0]);

        if (constructorInfo == null)
        {
            throw new Exception("The type '" + typeof(T) + "' does not have a default constructor");
        }

        return Expression.Lambda<Func<T>>(Expression.New(constructorInfo)).Compile();
    }

    public T CreateInstance()
    {
        if (_queue.Count == 0)
        {
            return CreateInstanceInternal();
        }

        if (_queue.Count <= _refreshThreshold && _runningRefresh == false)
        {
            _runningRefresh = true;
            ThreadPool.QueueUserWorkItem(RefreshCallback, this);
        }

        T queuedItem;

        _queue.TryDequeue(out queuedItem);

        return queuedItem;
    }

    private void RefreshCallback(object o)
    {
        var list = new List<T>();

        for (int i = _queue.Count; i < _size; ++i)
        {
            list.Add(CreateInstanceInternal());
        }

        list.ForEach(t => _queue.Enqueue(t));
        _runningRefresh = false;
    }

    private T CreateInstanceInternal()
    {
        return _callback == null ? _constructor() : _callback();
    }
}

Lets take a look at how we might use this. There are two basic usage scenarios that I can think of...

Activator Object Construction

First define an instance of the BackgroundActivator. In doing so you can specify the size of queued instances, and the size of the queue at which the it should be refilled.

var backgroundActivator = new BackgroundActivator<MyType>(200, 100);

Once we have an instance of the activator we can just call the CreateInstance method any time we need an instance of the object

var instance = backgroundActivator.CreateInstance();

Custom Object Construction

This scenario is ideal if your construction requirements are a bit more involved. In this case, a function is supplied which is called to perform the underlying construction.

var backgroundActivator = new BackgroundActivator<MyType>(200, 100, () =>
{
    var instance = new MyType();
    //TODO: Do more stuff
    return instance;
});

This is a pattern I use within my IoC Container and can really provide that little extra boost when you need it. Use in good health. :-)

Tags: ,

Comments

1.
trackback DotNetKicks.com says:

The Pre-emptively active Activator

You've been kicked (a good thing) - Trackback from DotNetKicks.com

Comments are closed