更新:在 @usr 指出我错误地假设后进行了重大修改Lazy<T>
的默认线程安全模式是LazyThreadSafetyMode.PublicationOnly
...
我想通过一个惰性计算值async
工厂方法(即它返回Task<T>
)并在成功时将其缓存。在例外情况下,我希望我可以使用它。然而我不想成为牺牲品异常缓存行为 http://theburningmonk.com/2013/04/be-lazy-but-be-ware-of-initialization-exception/ that Lazy<T>
处于默认模式(LazyThreadSafetyMode.ExecutionAndPublication
)
异常缓存:当您使用工厂方法时,异常会被缓存。也就是说,如果工厂方法在线程第一次尝试访问 Lazy 对象的 Value 属性时抛出异常,则每次后续尝试都会抛出相同的异常。这可确保每次调用 Value 属性都会产生相同的结果,并避免不同线程获得不同结果时可能出现的细微错误。 Lazy 代表实际的 T,否则该 T 会在较早的某个时刻(通常在启动期间)初始化。早期的失败通常是致命的。如果存在可恢复故障的可能性,我们建议您将重试逻辑构建到初始化例程(在本例中为工厂方法)中,就像不使用延迟初始化时一样。
斯蒂芬·图布有一个AsyncLazy课堂和写作 http://blogs.msdn.com/b/pfxteam/archive/2011/01/15/asynclazy-lt-t-gt.aspx这似乎是正确的:
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。如果多个线程同时调用 Value 属性,这使得 Lazy 对象能够在每个线程上创建延迟初始化对象的副本。 Lazy 对象确保所有线程都使用延迟初始化对象的同一实例,并丢弃未使用的实例。因此,减少锁定开销的代价是您的程序有时可能会创建和丢弃昂贵对象的额外副本。在大多数情况下,这是不可能的。 Lazy(LazyThreadSafetyMode) 和 Lazy(Func, LazyThreadSafetyMode) 构造函数的示例演示了此行为。
重要的
当您指定 PublicationOnly 时,即使指定工厂方法,也永远不会缓存异常。
有整箱吗,Nito.AsyncEx
或者类似的结构可能很适合这里?如果做不到这一点,任何人都可以看到一种优雅的方式来门控“正在进行的尝试”位(我同意每个调用者以与Lazy<T>(
..., (LazyThreadSafetyMode.PublicationOnly)
确实如此)但仍然有这个和缓存管理被整齐地封装?