从[N异步]工厂方法缓存结果当且仅当它不抛出 [英] caching the result from a [n async] factory method iff it doesn't throw

查看:130
本文介绍了从[N异步]工厂方法缓存结果当且仅当它不抛出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

更新:重修订后@usr指出我错误地假定懒< T> 的默认的线程安全模式是 LazyThreadSafetyMode.PublicationOnly ...

UPDATE: Heavily revised after @usr pointed out I'd incorrectly assumed Lazy<T>'s default thread safety mode was LazyThreadSafetyMode.PublicationOnly...

我想通过懒洋洋地计算值的异步工厂方法(即它返回任务&LT; T&GT; )并已将其成功时缓存。在例外,我希望有一个提供给我。我不却希望下跌preY来的例外缓存行为的是懒&LT; T&GT; 在其默认模式( LazyThreadSafetyMode.ExecutionAndPublication

I want to lazily compute a value via an async Factory Method (i.e. it returns Task<T>) and have it cached upon success. On exception, I want to have that be available to me. I do not however, want to fall prey to the exception caching behavior that Lazy<T> has in its default mode (LazyThreadSafetyMode.ExecutionAndPublication)

异常缓存:当您使用工厂方法,异常缓存。也就是说,如果工厂方法抛出一个异常,第一次一个线程试图访问懒惰对象的Value属性,同样的异常被抛出在随后每次尝试。这确保了Value属性每次调用产生相同的结果,避免了如果不同的线程得到不同的结果,可能会出现细微的错误。懒惰的代表为,否则将已在一些较早点通常在启动过程中初始化,实际吨。在更早一点的故障通常是致命的。如果有一个恢复故障潜在的,我们建议您打造的重试逻辑进入初始化程序(在这种情况下,工厂方法),就像你,如果你没有使用延迟初始化。

Exception caching: When you use factory methods, exceptions are cached. That is, if the factory method throws an exception the first time a thread tries to access the Value property of the Lazy object, the same exception is thrown on every subsequent attempt. This ensures that every call to the Value property produces the same result and avoids subtle errors that might arise if different threads get different results. The Lazy stands in for an actual T that otherwise would have been initialized at some earlier point, usually during startup. A failure at that earlier point is usually fatal. If there is a potential for a recoverable failure, we recommend that you build the retry logic into the initialization routine (in this case, the factory method), just as you would if you weren’t using lazy initialization.

斯蒂芬Toub有 AsyncLazy 类,并书面记录似乎恰到好处:

Stephen Toub has an AsyncLazy class and writeup that seems just right:

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

不过那是有效相同的行为作为默认懒&LT; T&GT; - 如果有问题,也不会有重试

however that's effectively the same behavior as a default Lazy<T> - if there's a problem, there will be no retries.

我在寻找一个任务&LT; T&GT; 懒&LT兼容同等学历; T&GT;(Func键&LT; T&GT ;, LazyThreadSafetyMode.PublicationOnly),也就是说,它应该表现为被指定: -

I'm looking for a Task<T> compatible equivalent of Lazy<T>(Func<T>, LazyThreadSafetyMode.PublicationOnly), i.e. it should behave as that is specified:-

替代锁定在某些情况下,你可能想避免懒对象的默认锁定行为的开销。在极少数情况下,有可能是死锁的可能性。在这种情况下,你可以使用懒惰(LazyThreadSafetyMode)或懒惰(FUNC键LazyThreadSafetyMode)构造函数,并指定LazyThreadSafetyMode.PublicationOnly。这使懒惰对象在每个多个线程的创建延迟初始化对象的副本,如果线程同时调用Value属性。懒惰的对象,确保所有线程使用延迟初始化对象的同一实例,并丢弃未使用的实例。因此,减少锁定开销的代价是你的程序有时会创建并放弃昂贵的对象的额外副本。在大多数情况下,这是不可能的。对于懒惰(LazyThreadSafetyMode)和懒惰的例子(FUNC键LazyThreadSafetyMode)构造函数说明这种情况。

Alternative to locking In certain situations, you might want to avoid the overhead of the Lazy object's default locking behavior. In rare situations, there might be a potential for deadlocks. In such cases, you can use the Lazy(LazyThreadSafetyMode) or Lazy(Func, LazyThreadSafetyMode) constructor, and specify LazyThreadSafetyMode.PublicationOnly. This enables the Lazy object to create a copy of the lazily initialized object on each of several threads if the threads call the Value property simultaneously. The Lazy object ensures that all threads use the same instance of the lazily initialized object and discards the instances that are not used. Thus, the cost of reducing the locking overhead is that your program might sometimes create and discard extra copies of an expensive object. In most cases, this is unlikely. The examples for the Lazy(LazyThreadSafetyMode) and Lazy(Func, LazyThreadSafetyMode) constructors demonstrate this behavior.

重要提示

在指定PublicationOnly,异常不会被缓存,即使您指定的工厂方法。

When you specify PublicationOnly, exceptions are never cached, even if you specify a factory method.

有没有整箱, Nito.AsyncEx 或类似的结构可能会很好地适应这里?如果做不到这一点,任何人都可以看到优雅的方式来门企图进行位(我与每个来电者以同样的方式做出自己的努力确定一个懒&LT; T&GT;( ... (LazyThreadSafetyMode.PublicationOnly)一样),但仍然有整齐封装的高速缓存管理?

Is there any FCL, Nito.AsyncEx or similar construct that might fit in nicely here? Failing this, can anyone see an elegant way to gate the "attempt in progress" bit (I'm OK with each caller making its own attempt in the same way that a Lazy<T>( ..., (LazyThreadSafetyMode.PublicationOnly) does) and yet still have that and the cache management encapsulated neatly?

推荐答案

这是否让你附近的任何地方的要求?

Does this get anywhere near your requirements?

行为属于介于 ExecutionAndPublication PublicationOnly

虽然初始值设定在飞行到值所有呼叫将交由同一个任务(这是暂时缓存,但随后可能会成功或失败);如果初始化成功,则该完成的任务永久缓存;如果初始化失败,那么下一个呼叫将创建一个全新的初始化任务和过程又重新开始!

While the initializer is in-flight all calls to Value will be handed the same task (which is cached temporarily but could subsequently succeed or fail); if the initializer succeeds then that completed task is cached permanently; if the initializer fails then the next call to Value will create a completely new initialization task and the process begins again!

public sealed class TooLazy<T>
{
    private readonly object _lock = new object();
    private readonly Func<Task<T>> _factory;
    private Task<T> _cached;

    public TooLazy(Func<Task<T>> factory)
    {
        if (factory == null) throw new ArgumentNullException("factory");
        _factory = factory;
    }

    public Task<T> Value
    {
        get
        {
            lock (_lock)
            {
                if ((_cached == null) ||
                    (_cached.IsCompleted && (_cached.Status != TaskStatus.RanToCompletion)))
                {
                    _cached = Task.Run(_factory);
                }
                return _cached;
            }
        }
    }
}

这篇关于从[N异步]工厂方法缓存结果当且仅当它不抛出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆