我试图异步填充我的缓存
static ConcurrentDictionary<string, string[]> data = new ConcurrentDictionary<string, string[]>();
public static async Task<string[]> GetStuffAsync(string key)
{
return data.GetOrAdd(key, async (x) => {
return await LoadAsync(x);
});
}
static async Task<string[]> LoadAsync(string key) {....}
但这给了我错误:
无法将异步lambda表达式转换为委托类型&#39; System.Func&#39;。
异步lambda表达式可能返回void,Task或Task,其中任何一个都不能转换为&System; System.Func&#39;。
据我所知,这是因为GetOrAdd()
不是异步的。我该如何解决这个问题?
更新
评论中提出的LazyAsync
将在我的琐碎例子中起作用。或者,像这样的解决方法(肯定会带来它引入的一些开销):
public static async Task<string[]> GetStuffAsync(string key)
{
string[] d = null;
if (!data.ContainsKey(key))
d = await LoadAsync(key);
return data.GetOrAdd(key, d);
}
问题就变成微软没有时间更新所有接口来支持异步,或者我试图做一些非常错误的事情(ConcurrentDictionary
不应该GetOrAddAsync()
)?
答案 0 :(得分:19)
异步方法(或lambda)只能返回void
或Task
或Task<T>
,但你的lambda会返回string[]
,因此编译器会阻止你。
await
关键字已经过优化,可以在任务完成后同步继续。因此,一种选择是将任务本身存储在字典中,而不必担心一次又一次地等待已完成的任务。
private static ConcurrentDictionary<string, Task<string[]>> data =
new ConcurrentDictionary<string, Task<string[]>>();
public static Task<string[]> GetStuffAsync(string key)
{
return data.GetOrAdd(key, LoadAsync);
}
当你做的时候
var item = await GetStuffAsync(...);
第一次它将(a)等待缓存的任务完成 - 之后它会同步继续。
您必须考虑LoadAsync
失败时会发生什么。因为我们正在缓存LoadAsync
返回的任务;如果失败了,我们将愚蠢地缓存失败的任务。您可能需要处理此问题。