【问题标题】:Purpose of Lazy<T> on this MSDN Example此 MSDN 示例中 Lazy<T> 的用途
【发布时间】:2014-10-21 04:38:48
【问题描述】:

我一直在阅读有关此 Asynchronous article from MSDN 的信息,但我无法理解给定示例中 Lazy&lt;T&gt; 的用途。

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

据我了解,当您调用.ValueLazy&lt;T&gt; 时,它会调用其中的构造函数。从例子看是马上调用的,为什么要加Lazy&lt;T&gt;呢?

【问题讨论】:

    标签: c# asynchronous lazy-initialization


    【解决方案1】:

    假设您将其修改为不使用Lazy&lt;T&gt;

    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:

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

    因此,如果同时发生对同一个键的多次访问,则可能会为同一个键多次调用_valueFactory

    现在如何使用Lazy&lt;T&gt; 解决问题?尽管并发调用可能会创建多个Lazy&lt;Task&lt;TValue&gt;&gt; 实例,但从GetOrAdd 只会返回一个。所以只有一个人可以访问其Value 属性。所以每个键只会调用一次_valueFactory

    这当然是一个理想的功能。如果我创建了一个使用 lambda url =&gt; DownloadFile(url) 创建的 AsyncCache&lt;string, byte[]&gt; cache,我不希望有一堆并发请求到 cache[myUrl] 来多次下载文件。

    【讨论】:

      【解决方案2】:

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

      更完整的答案:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多