同步函数内的异步调用

时间:2015-01-21 12:03:18

标签: c# .net 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) {....}

但这给了我错误:

  

无法将异步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() )?

1 个答案:

答案 0 :(得分:19)

异步方法(或lambda)只能返回voidTaskTask<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返回的任务;如果失败了,我们将愚蠢地缓存失败的任务。您可能需要处理此问题。