кэширование результата от фабричного метода [n async], если он не генерирует

ОБНОВЛЕНИЕ: Сильно исправленный после того, как @usr указал, что я неправильно предположилLazy<T>режим безопасности потока по умолчанию былLazyThreadSafetyMode.PublicationOnly...

Я хочу лениво вычислить значение черезasync&nbsp;Фабричный метод (т.е. он возвращаетTask<T>) и кешировать на успех. В порядке исключения я хочу, чтобы это было доступно для меня. Я, однако, не хочу стать жертвойповедение при кэшировании исключений&nbsp;тотLazy<T>&nbsp;в режиме по умолчанию (LazyThreadSafetyMode.ExecutionAndPublication)

Кэширование исключений: когда вы используете фабричные методы, исключения кэшируются. То есть, если метод фабрики выдает исключение в первый раз, когда поток пытается получить доступ к свойству Value объекта Lazy, то же исключение выдается при каждой последующей попытке. Это гарантирует, что каждый вызов свойства Value приводит к одному и тому же результату, и позволяет избежать незначительных ошибок, которые могут возникнуть, если разные потоки получают разные результаты. Ленивый заменяет фактический T, который в противном случае был бы инициализирован в более ранней точке, обычно во время запуска. Отказ в этой более ранней точке обычно фатален. Если существует вероятность восстанавливаемого сбоя, мы рекомендуем встроить логику повтора в процедуру инициализации (в данном случае, в метод фабрики), как если бы вы не использовали отложенную инициализацию.

Стивен Туб имеетAsyncLazy&nbsp;класс и рецензия&nbsp;это кажется правильным:

public class AsyncLazy<T> : Lazy<Task<T>>
{
    public AsyncLazy(Func<Task<T>> taskFactory) :
        base(() => Task.Factory.StartNew(() => taskFactory()).Unwrap())
    { }

    public TaskAwaiter<T> GetAwaiter() { return Value.GetAwaiter(); }
}

Однако это фактически то же поведение, что и по умолчаниюLazy<T>&nbsp;- если есть проблема, повторных попыток не будет.

Я ищуTask<T>&nbsp;совместимый эквивалентLazy<T>(Func<T>, LazyThreadSafetyMode.PublicationOnly)то есть он должен вести себя так, как указано:

Альтернатива блокировке В некоторых ситуациях вы можете избежать издержек, связанных с поведением блокировки объекта Lazy по умолчанию. В редких ситуациях возможны тупики. В таких случаях вы можете использовать конструктор Lazy (LazyThreadSafetyMode) или Lazy (Func, LazyThreadSafetyMode) и указать LazyThreadSafetyMode.PublicationOnly. Это позволяет объекту Lazy создавать копию лениво инициализированного объекта в каждом из нескольких потоков, если потоки одновременно вызывают свойство Value. Объект Lazy гарантирует, что все потоки используют один и тот же экземпляр лениво инициализированного объекта, и отбрасывает неиспользуемые экземпляры. Таким образом, стоимость сокращения накладных расходов на блокировку заключается в том, что ваша программа может иногда создавать и отбрасывать дополнительные копии дорогостоящего объекта. В большинстве случаев это маловероятно. Примеры для конструкторов Lazy (LazyThreadSafetyMode) и Lazy (Func, LazyThreadSafetyMode) демонстрируют это поведение.

ВАЖНЫЙ

Когда вы указываете PublicationOnly, исключения никогда не кэшируются, даже если вы указали фабричный метод.

Есть ли FCL,Nito.AsyncEx&nbsp;или подобная конструкция, которая могла бы хорошо здесь вписаться? Если это не удастся, может кто-нибудь увидеть элегантный способ скрыть бит «попытка в процессе» (я в порядке, когда каждый вызывающий абонент делает свою собственную попытку так же, какLazy<T>(&nbsp;...,(LazyThreadSafetyMode.PublicationOnly)&nbsp;делает) и все же все еще есть это, и управление кешем инкапсулировано аккуратно?