在此MSDN示例上的Lazy <T>的目的

Alb*_*ert 4 c# asynchronous lazy-initialization

我一直在阅读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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

根据我的理解,当你调用.Value时Lazy<T>,它将调用内部的构造函数.从示例中,它立即被调用,为什么要添加Lazy<T>?

Tim*_*lds 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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请参阅文档中的备注:

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

因此,_valueFactory如果同时对同一个密钥进行多次访问,则可能会多次调用相同的密钥.

现在如何使用Lazy<T>修复问题?虽然Lazy<Task<TValue>>并发调用可能会创建多个实例,但只会返回一个实例GetOrAdd.因此,只有一个人可以Value访问其属性.因此_valueFactory每个键只会发生一次调用.

这当然是一个理想的特征.如果我AsyncCache<string, byte[]> cache创建了一个用lambda创建的url => DownloadFile(url),我不希望一堆并发请求cache[myUrl]多次下载文件.