Асинхронные задачи оцениваются дважды [c#]


Answers

Вы должны помнить, что операции LINQ возвращают запросы, а не результаты этих запросов. Результаты переменных не представляют результаты операции, которые у вас есть, а скорее запрос, который способен генерировать эти результаты при повторе . Вы повторяете его дважды, выполняя запрос в каждом из этих случаев.

То, что вы можете сделать здесь, вначале материализует результаты запроса в коллекцию, а не хранит сам запрос, в results .

var results = queries.Select(query => Tuple.Create(query, LoadDataAsync(query)))
    .ToList();

await Task.WhenAll(results.Select(x => x.Item2));

return results
    .ToDictionary(x => x.Item1, x => x.Item2.Result);
Question

Я использую следующий метод для выполнения некоторых задач асинхронно и одновременно:

public async Task<Dictionary<string, object>> Read(string[] queries)
{
    var results = queries.Select(query => new Tuple<string, Task<object>>(query, LoadDataAsync(query)));

    await Task.WhenAll(results.Select(x => x.Item2).ToArray());

    return results
        .ToDictionary(x => x.Item1, x => x.Item2.Result);
}

Я хочу, чтобы метод вызывал LoadDataAsync для каждой строки в массиве одновременно, а затем дождался завершения всех задач и возврата результата.

  • Если я запустил такой метод, он дважды запускает LoadDataAsync для каждого элемента, один раз в строке await ... и один раз в конечном .Result .
  • Если я удалю строку await ... , Visual Studio предупреждает меня, что весь метод будет работать параллельно, поскольку внутри метода нет await .

Что я делаю не так?

Есть ли лучшие (более короткие) способы сделать то же самое?




В дополнение к ответу Джесси Свитланда , полностью реализованная версия:

public async Task<KeyValuePair<string, object>> LoadNamedResultAsync(string query)
{
    Task<object> getLoadDataTask = await LoadDataAsync(query);
    return new KeyValuePair<string, object>(query, getLoadDataTask.Result);
}

public async Task<IDictionary<string, object>> Read(string[] queries)
{
    var tasks = queries.Select(LoadNamedResultAsync);
    var results = await Task.WhenAll(tasks);
    return results.ToDictionary(r => r.Key, r => r.Value);
}

Rem: Я предложил это как редактировать, но он был отклонен из-за слишком больших изменений.