同步函数内的异步调用

Async call within synchronous function

我正在尝试异步填充缓存

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) {....}

但这给了我错误:

Cannot convert async lambda expression to delegate type 'System.Func'.

An async lambda expression may return void, Task or Task, none of which are convertible to '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);
}

然后问题就变成了微软是否没有时间更新所有接口以支持异步,或者我正在尝试做一些严重错误的事情(而且 ConcurrentDictionary 不应该 GetOrAddAsync())?

异步方法(或 lambda)只能 return voidTaskTask<T> 但您的 lambda returns 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 编辑的 return 任务;如果失败,我们将愚蠢地缓存失败的任务。您可能需要处理这个问题。