Интересно, является ли решение async / await чистым

#c# #asynchronous #task-parallel-library

#c# #асинхронный #задача-параллельная-библиотека

Вопрос:

Я пришел к следующему коду в одном из моих базовых классов, который отлично работает (см. Комментарий):

 private static Dictionary<string, TItem> _cache;

protected Dictionary<string, TItem> Cache
{
    get
    {
        if (_cache == null)
        {
            // Instead of FillCacheAsync().Wait(); I now do the following:
            var reset = new AutoResetEvent(false);
            Task.Run(
                async () =>
                {
                    await FillCacheAsync();
                    reset.Set();
                });
            reset.WaitOne();
        }
        return _cache;
    }
}

private async Task FillCacheAsync()
{            
    _cache = new Dictionary<string, TItem>();
     await InternalCacheFillAsync();            
}

// This is not part of the question.
protected abstract Task InternalCacheFillAsync();
  

Является ли это хорошим решением проблемы, из-за которой отложенное свойство не может быть помечено как async ? Здесь у меня были огромные проблемы даже с использованием ConfigureAwait(false) на месте.

Комментарии:

1. Почему бы не использовать simple FillCacheAsync().Wait() ? В чем преимущество этой дополнительной работы с событием.

2. @Honza: Как я писал в своем посте, это приводит к серьезным проблемам, даже если я тщательно использовал ConfigureAwait весь путь вниз.

3. AutoResetEvent На самом деле в этом нет необходимости; лучшим решением для блокировки было бы Task.Run(() => FillCacheAsync()).GetAwaiter().GetResult() . Было бы лучше вообще не блокировать — например, AsyncLazy<T> .

Ответ №1:

Стивен Клири освещает именно эту тему свойств async в своем блоге.

Он предлагает простой способ удовлетворить вашу потребность в свойстве cached value, которое будет кэшироваться асинхронным способом с использованием AsyncLazy<T> его библиотеки AsyncEx, это избавит вас от необходимости использовать AutoResetEvent и упростит ваш код.

Простой пример вашего класса с кэшированным свойством:

 public class SomeClass
{
    static SomeClass()
    { 
        Cache = new AsyncLazy<Dictionary<string, TItem>>(GetCacheAsync);
    }

    public static AsyncLazy<Dictionary<string, TItem>> Cache { get; }

    private static Task<Dictionary<string, TItem>> GetCacheAsync()
    {
        ....
    }
}. 
  

И доступ к значению кэшированного свойства:

 Dictionary<string, TItem> value = await SomeClass.Cache;
  

Комментарии:

1. Хороший материал! Итак, я предполагаю, что мое решение не такое уж и сложное, но все еще действительное. Я в любом случае буду использовать библиотеку Стивенса. Кажется, он знает намного больше об этих вещах, чем я :-).

2. Я не могу рекомендовать больше о его блоге, каждая статья там — кусок золота. Его книга тоже должна быть хорошей, хотя у меня пока нет времени ее прочитать.

3. Просто побочное замечание: используйте более новую AsyncEx.Coordination библиотеку , если можете. Более старая AsyncEx библиотека скоро переключится на новую версию.