16handles

Asynchronous Lazy Initialization

Posted on: April 21, 2011

Framework Class Library has pretty simple and useful class for lazy initialization: System.Lazy.
Constructor of the class takes a delegate which will create an object when some code request it.

class Program
{
	private static Lazy<HeavyObject> _heavyObject = new Lazy<HeavyObject>(ObjectFactory);

	static void Main(string[] args)
	{
		Console.WriteLine("Accessing heavy object");
		HeavyObject heavyObject = _heavyObject.Value;
		heavyObject.SayHello();
	}

	static HeavyObject ObjectFactory()
	{
		Console.WriteLine("Creating heavy object");
		Thread.Sleep(2000);

		return new HeavyObject();
	}
}

class HeavyObject
{
	public void SayHello()
	{
		Console.WriteLine("Hello!");
	}
}

That’s great, but sometimes we want to manually start instantiation process. For this purpose I developed new class, AsyncLazy<T>.

It has almost the same structure as Lazy<T>:

IsValueCreated allows you to determine whether object have been instantiated.

Value property checks whether object has already been instantiated, and, if yes, returns it. Otherwise calling thread is blocked until object will not be created.

InitializeAsync method makes a difference with Lazy<T> . The method should be called when you feel that Value will be accessed in nearest time. InitializeAsync begins instantiation of the object on different thread, so, calling thread won’t be blocked.

Here is a code:

/// <summary>
/// Provides support for lazy initialization in asyncronous manner.
/// </summary>
/// <typeparam name="T">Specifies the type of object that is being lazily initialized.</typeparam>
public sealed class AsyncLazy<T>
{
    #region Fields

    private object _syncObject = new object();
    private Task<T> _initializeTask;

    #endregion

    #region Constructors

    /// <summary>
    /// Initializes a new instance of the <see cref="AsyncLazy"/> class.
    /// When lazy initialization occurs, the specified initialization function is used.
    /// </summary>
    /// <param name="valueFactory">The delegate that is invoked to produce the lazily initialized value when
    /// it is needed.</param>
    public AsyncLazy(Func<T> valueFactory)
    {
        if (valueFactory == null)
        {
            throw new ArgumentNullException("valueFactory");
        }
        _initializeTask = new Task<T>(valueFactory);
    }

    #endregion

    /// <summary>
    ///  Gets the lazily initialized value of the current instance.
    ///  If value have not been initialized, block calling thread until value get initialized.
    ///  If during initialization exception have been thrown, it will wrapped into <see cref="AggregateException"/>
    ///  and rethrowned on accessing this property.
    /// </summary>
    public T Value
    {
        get
        {
            if (_initializeTask.Status == TaskStatus.Created)
            {
                lock (_syncObject)
                {
                    if (_initializeTask.Status == TaskStatus.Created)
                    {
                        _initializeTask.RunSynchronously();
                    }
                }
            }

            return _initializeTask.Result;
        }
    }

    /// <summary>
    /// Gets a value that indicates whether a value has been created for this instance.
    /// </summary>
    /// <value>
    /// 	<c>true</c> if a value has been created; otherwise, <c>false</c>.
    /// </value>
    public bool IsValueCreated
    {
        get
        {
            return _initializeTask.IsCompleted;
        }
    }

    /// <summary>
    /// Initializes value on background thread.
    /// Calling thread will never be blocked.
    /// </summary>
    public void InitializeAsync()
    {
        if (_initializeTask.Status == TaskStatus.Created)
        {
            lock (_syncObject)
            {
                if (_initializeTask.Status == TaskStatus.Created)
                {
                    _initializeTask.Start();
                }
            }
        }
    }
}

In background, AsyncLazy contains a Task, which executed asynchronously (when InitializeAsync was called first) or synchronously (when Value was accessed first). Result property of a Task blocks calling thread until task get completed, this is providing us a simple synchronization mechanism.

Typical use case looks like that:

private static AsyncLazy<HeavyObject> _heavyObject = new AsyncLazy<HeavyObject>(ObjectFactory);

static void Main(string[] args)
{
	Console.WriteLine("Doing some work");

	Console.WriteLine("It's time to begin initialize heavy object!");
	_heavyObject.InitializeAsync();

	Console.WriteLine("Accessing heavy object");
	HeavyObject heavyObject = _heavyObject.Value;
	heavyObject.SayHello();
}
About these ads

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

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: