UPDATE: сильно изменился после того, как @usr указал, что я неправильно принял Lazy<T>
режим безопасности по умолчанию по умолчанию был LazyThreadSafetyMode.PublicationOnly
...
Я хочу лениво вычислить значение с помощью метода async
Factory (т.е. возвращает Task<T>
) и кэшировать его при успешном завершении. В случае исключения я хочу, чтобы это было доступно мне. Однако я не хочу жертвовать поведение кэширования исключений, которое Lazy<T>
имеет в своем режиме по умолчанию (LazyThreadSafetyMode.ExecutionAndPublication
)
Кэширование исключений. Когда вы используете методы Factory, исключения кэшируются. То есть, если метод Factory генерирует исключение, первый раз, когда поток пытается получить доступ к свойству Value объекта Lazy, одно и то же исключение бросается на каждую последующую попытку. Это гарантирует, что каждый вызов свойства Value дает тот же результат и избегает тонких ошибок, которые могут возникнуть, если разные потоки получают разные результаты. Lazy стоит за фактическое T, которое иначе было бы инициализировано в какой-то более ранней точке, обычно во время запуска. Сбой в этом более раннем пункте обычно является фатальным. Если есть потенциал для восстанавливаемого сбоя, мы рекомендуем построить логику повтора в процедуре инициализации (в данном случае, метод Factory), так же, как если бы вы не использовали ленивую инициализацию.
Стивен Тууб имеет класс AsyncLazy
и запись, который выглядит правильно:
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>
- если есть проблема, повторений не будет.
Я ищу эквивалент Task<T>
Lazy<T>(Func<T>, LazyThreadSafetyMode.PublicationOnly)
, т.е. он должен вести себя так, как указано: -
Альтернатива блокировке В определенных ситуациях вы можете избежать накладных расходов на поведение блокировки по умолчанию Lazy. В редких случаях может возникнуть вероятность блокировок. В таких случаях вы можете использовать конструктор Lazy (LazyThreadSafetyMode) или Lazy (Func, LazyThreadSafetyMode) и указать LazyThreadSafetyMode.PublicationOnly. Это позволяет объекту Lazy создавать копию лениво инициализированного объекта для каждого из нескольких потоков, если потоки одновременно называют свойство Value. Объект Lazy гарантирует, что все потоки используют один и тот же экземпляр лениво инициализированного объекта и отбрасывают экземпляры, которые не используются. Таким образом, стоимость сокращения накладных расходов на блокирование заключается в том, что иногда ваша программа может создавать и отбрасывать дополнительные копии дорогостоящего объекта. В большинстве случаев это маловероятно. Эти примеры демонстрируют примеры конструкторов Lazy (LazyThreadSafetyMode) и Lazy (Func, LazyThreadSafetyMode).
ВАЖНО
Когда вы указываете PublicationOnly, исключения никогда не кэшируются, даже если вы укажете метод Factory.
Есть ли какой-либо FCL, Nito.AsyncEx
или подобный конструкт, который может быть здесь очень хорош? В противном случае кто-нибудь может увидеть элегантный способ заблокировать бит "попытка в процессе" (я в порядке с каждым вызывающим, делающим свою попытку таким же образом, что и Lazy<T>(
..., (LazyThreadSafetyMode.PublicationOnly)
), и все же все еще есть, и управление кэшем инкапсулировано аккуратно?