Как настроить Unity для асинхронной инициализации типов / модулей - PullRequest
1 голос
/ 29 августа 2011

Как настроить Unity таким образом, чтобы класс мог инициализироваться асинхронно, не блокируя загрузку других модулей (только блокируя другие типы, которые явно нуждаются в экземпляре асинхронного типа)? Типы классов, о которых я думаю, - это кэши справочных данных, которые извлекают снимок часто используемых данных из базы данных, и мне нужно закончить предварительное кэширование, прежде чем разрешить доступ к нему любым другим модулям (если запросы заблокированы в моем классе I). быстро задержит основной поток и заблокирует все остальные модули от инициализации). Это становится более важным, поскольку у меня есть несколько таких классов справочных данных

Например, скажем, у меня есть такой класс:

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 */
    }
}

Если я вызову Initialize из конструктора, я заблокирую поток, который его вызывает (из Unity) на 30 секунд, не давая мне возможности создавать другие (аналогичные) классы параллельно. Если я просто помещу задачу в пул потоков, Unity в конечном итоге доберется до точки, где другой класс, которому необходим мой кэш продукта, выполняет свой код инициализации, а затем получает доступ к структурам данных, которые еще не полностью инициализированы (в этом случае это приведет к в промахе кеша и вызове в БД для получения конкретного продукта, а таких запросов может быть много за 30 секунд)

спасибо Oskar

1 Ответ

1 голос
/ 08 сентября 2011

Вам необходимо составить список запущенных задач, выполнить их параллельно и использовать Task.WaitAll (), чтобы дождаться их завершения, прежде чем продолжить.

В .Net 4 это должно работать, и обработка ошибок упрощается:

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!");
    }
}

В старой школе .Net попробуйте это. Я предполагал, что вы используете интерфейс для каждого инициализируемого объекта, и я исключил обработку ошибок:

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());
}
...