此MSDN示例</t>上的Lazy <t>的目的

时间:2014-10-21 04:38:49

标签: c# asynchronous lazy-initialization

我一直在阅读Asynchronous article from 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;
        }
    }
}

根据我的理解,当您致电.Value Lazy<T>时,它会调用其中的构造函数。从示例中,它立即被调用,所以为什么要添加Lazy<T>

2 个答案:

答案 0 :(得分: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));
        }
    }
}

See the remarks in the documentation:

  

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

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

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

这当然是一个理想的功能。如果我创建了使用lambda AsyncCache<string, byte[]> cache创建的url => DownloadFile(url),我不希望向cache[myUrl]发送多个并发请求以多次下载该文件。

答案 1 :(得分:0)

并发字典可以多次调用GetOrAdd的create lambda,但该值只会添加一次。这将导致只创建一次惰性值。

更完整的答案:

假设您有两个主题。两者都调用GetOrAdd方法,都执行GetOrAdd方法的create lambda。此时,他们都会尝试将新的键值添加到存储桶中。只有一个线程会成功将值添加到存储桶,另一个线程将失败,然后执行get命令。此时它将检索第一个线程创建的值。它们都将访问相同Lazy对象的值,而_valueFactory(toAdd)将被执行一次。