Async call within synchronous function

2019-02-13 12:09发布

I'm trying to populate my cache asynchronously

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

but this gives me the error:

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'.

As I understand this is because GetOrAdd() is not asynchronous. How can I fix the issue?

Update: LazyAsync suggested in the comments will work in my trivial example. Or, workaround like this (can definitely live with some overhead it introduces):

public static async Task<string[]> GetStuffAsync(string key)
{
    string[] d = null;
    if (!data.ContainsKey(key))
        d = await LoadAsync(key);
    return data.GetOrAdd(key, d);
}

The question then becomes did Microsoft just have no time to update all interfaces to support async or I'm trying to do something deeply wrong (and ConcurrentDictionary shouldn't have GetOrAddAsync()) ?

1条回答
叛逆
2楼-- · 2019-02-13 12:59

Async methods (or lambda) can only return void or Task or Task<T> but your lambda returns string[] and thus compiler prevents you.

await keyword is optimized to continue synchronously when the Task is already completed. So, one option is to store the Task itself in dictionary and don't worry about awaiting the completed Task again and again.

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

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

And when you do

var item = await GetStuffAsync(...);

first time it will (a)wait till the cached Task finishes --There after it will continue synchronously.

You'll have to think about what should happen when LoadAsync fails. Because we're caching the Task returned by LoadAsync; if that fails we'll foolishly cache the failed Task. You may need to handle this.

查看更多
登录 后发表回答