同步函数内的异步调用

Use*_*rol 10 .net c# asynchronous async-await

我正在尝试异步填充我的缓存

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) {....}
Run Code Online (Sandbox Code Playgroud)

但这给了我错误:

无法将异步lambda表达式转换为委托类型'System.Func'.

异步lambda表达式可能返回void,Task或Task,其中任何一个都不能转换为'System.Func'.

据我所知,这是因为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);
}
Run Code Online (Sandbox Code Playgroud)

那么问题就变成微软没有时间更新所有接口来支持异步,或者我正在尝试做一些非常错误的事情(而且ConcurrentDictionary不应该有GetOrAddAsync())?

Sri*_*vel 23

异步方法(或lambda)只能返回voidTaskTask<T>,但你的拉姆达的回报string[],因此编译器会阻止您.

await关键字已优化为在任务已完成时同步继续.因此,一种选择是将Task本身存储在字典中,而不必担心一次又一次地等待已完成的Task.

private static ConcurrentDictionary<string, Task<string[]>> data =
    new ConcurrentDictionary<string, Task<string[]>>();

public static Task<string[]> GetStuffAsync(string key)
{
    return data.GetOrAdd(key, LoadAsync);
}
Run Code Online (Sandbox Code Playgroud)

而当你这样做

var item = await GetStuffAsync(...);
Run Code Online (Sandbox Code Playgroud)

第一次它将(a)等待缓存的任务完成 - 之后它将继续同步.

你必须考虑LoadAsync失败时会发生什么.因为我们正在缓存返回的任务LoadAsync; 如果失败了,我们将愚蠢地缓存失败的任务.您可能需要处理此问题.

  • 实际上,您甚至可以编写return data.GetOrAdd(key,LoadAsync); :) (2认同)