这个MSDN示例中Lazy<T>的目的是什么?

4

我一直在阅读这篇来自MSDN的异步文章,但我无法理解给定示例中Lazy<T>的用途。

public class AsyncCache<TKey, TValue>
{
    private readonly Func<TKey, Task<TValue>> _valueFactory;
    private readonly ConcurrentDictionary<TKey, Lazy<Task<TValue>>> _map;

    public AsyncCache(Func<TKey, Task<TValue>> valueFactory)
    {
        if (valueFactory == null) throw new ArgumentNullException("loader");
        _valueFactory = valueFactory;
        _map = new ConcurrentDictionary<TKey, Lazy<Task<TValue>>>();
    }

    public Task<TValue> this[TKey key]
    {
        get
        {
            if (key == null) throw new ArgumentNullException("key");
            return _map.GetOrAdd(key, toAdd => 
                new Lazy<Task<TValue>>(() => _valueFactory(toAdd))).Value;
        }
    }
}

据我所知,当您调用Lazy<T>.Value时,它会调用构造函数。从示例中可以看出,它会立即调用,那为什么要添加Lazy<T>呢?
2个回答

5
假设您修改它,不使用Lazy<T>
public class AsyncCache<TKey, TValue>
{
    private readonly Func<TKey, Task<TValue>> _valueFactory;
    private readonly ConcurrentDictionary<TKey, Task<TValue>> _map;

    public AsyncCache(Func<TKey, Task<TValue>> valueFactory)
    {
        if (valueFactory == null) throw new ArgumentNullException("loader");
        _valueFactory = valueFactory;
        _map = new ConcurrentDictionary<TKey, Task<TValue>>();
    }

    public Task<TValue> this[TKey key]
    {
        get
        {
            if (key == null) throw new ArgumentNullException("key");
            return _map.GetOrAdd(key, toAdd => _valueFactory(toAdd));
        }
    }
}

请查看文档中的注释:

如果在不同的线程上同时调用GetOrAdd,则可能会多次调用addValueFactory,但其键/值对可能不会被添加到字典中。

因此,如果同时对同一键进行多次访问,则可能会多次调用_valueFactory

那么,使用Lazy<T>是如何解决这个问题的呢?虽然并发调用可能会创建多个Lazy<Task<TValue>>实例,但是只有一个实例会从GetOrAdd返回。因此,只有一个实例的Value属性将被访问。所以对于每个键,_valueFactory只会被调用一次。

当然,这是一个令人称赞的特性。如果我创建了一个使用lambda表达式url => DownloadFile(url)作为参数的AsyncCache<string, byte[]> cache,我不希望并发请求cache[myUrl]多次下载文件。


0

并发字典可能会多次调用GetOrAdd的创建lambda,但值只会被添加一次。这将导致延迟值仅被创建一次。

一个更完整的答案:

假设你有两个线程。两者都调用GetOrAdd方法,两者都执行GetOrAdd方法的创建lambda。在这一点上,它们都会尝试将新的键值对添加到桶中。只有一个线程能成功将值添加到桶中,另一个线程将失败,然后执行获取命令。此时,它将检索到第一个线程创建的值。它们都将访问相同的Lazy对象的值,_valueFactory(toAdd)将被执行一次。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接