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