懒惰的目的<;T>;在这个MSDN示例中
本文关键字:MSDN lt gt | 更新日期: 2023-09-27 18:27:11
我一直在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>
呢?
假设您将其修改为不使用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]
的并发请求多次下载该文件。
并发字典可能会多次调用GetOrAdd的create lambda,但该值只会添加一次。这将导致懒惰值只创建一次。
更完整的答案:
假设你有两条线索。两者都调用了GetOrAdd方法,都执行了GetOrAdd方法的create lambda。此时,他们都将尝试将新的键值添加到bucket中。只有一个线程会成功地将值添加到bucket中,另一个线程将失败,然后执行get命令。此时,它将检索第一个线程创建的值。它们都将访问同一个Lazy对象的值,并且_valueFactory(toAdd)将被执行一次。