Oskar
Oskar

Reputation: 2314

How to configure Unity for asynch initialization of types / modules

How do I set up Unity such that a class can intialize asynchronously without blocking the load of other modules (only blocking other types that explicitly need an instance of the asynch type)? The kind of classes I'm thinking of are reference data caches that pull in a snapshot of frequently used data from the database, and I need to finish precaching before I let any other modules access it (if the requests are blocked within my class I will quickly hold up the main thread and block all other modules from initializing). This becomes more important as I have multiple such reference data classes

As an example, say I have a class like this:

public class ProductCache{

    public ProductCache(){}

    public Initialize(){
        // a very slow DB call to fetch frequently used products
        Thread.Sleep(30*1000);
    }

    public Product FindProduct(string productDescription){
        /* check cache, if not there try the db */
    }
}

If I call Initialize from the constructor I will block the thread that invokes it (from Unity) for 30 seconds, preventing me from creating other (similar) classes in parallel. If I simply put the task on the threadpool Unity will eventually get to the point where another class that needs my product cache is executing its initialization code, and it is then accessing datastructures that aren't fully initialized yet (in this case it would result in a cache miss and a call to the db to fetch the particular product, and there might be many such requests in 30 seconds)

thanks Oskar

Upvotes: 1

Views: 745

Answers (1)

Andrew Bain
Andrew Bain

Reputation: 71

You need to make a list of the running tasks, execute them in parallel, and use Task.WaitAll() to wait for them to complete before continuing.

In .Net 4, this should work, and error handling is made easy:

public void InitializeAll()
{
    List<Task> initTasks = new List<Task>();

    ProductCache productCache = new ProductCache();
    SomeOtherCache someOtherCache = new SomeOtherCache();

    initTasks.Add(Task.Factory.StartNew(() => productCache.Initialize()));
    initTasks.Add(Task.Factory.StartNew(() => someOtherCache.Initialize()));

    try
    {
        Task.WaitAll(initTasks.ToArray());
    }
    catch (AggregateException ex)
    {
        Console.WriteLine("Oh dear!");
    }
}

In old-school .Net, try this. I've assumed you're using an interface for each initializable object, and I've left out error handling:

public void InitializeAll(IEnumerable<IInitializable> initializeUs)
{
    List<WaitHandle> handles = new List<WaitHandle>();

    foreach(IInitializable init in initializeUs)
    {
        // Make a copy of the reference in the local scope
        IInitializable temp = init;

        ManualResetEvent done = new ManualResetEvent(false);
        handles.Add(done);

        ThreadPool.QueueUserWorkItem(delegate
        {
            try
            {
                temp.Initialize();
            }
            finally
            {
                done.Set();
            }
        });
    }

    // Wait for all the handles to be set
    WaitHandle.WaitAll(handles.ToArray());
}

Upvotes: 1

Related Questions