我一直在阅读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>
?
答案 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)将被执行一次。