Я вижу некоторые странные поведенческие различия при вызове Task.WhenAll(IEnumerable<Task<T>>)
и вызове Task.WhenAll(List<Task<T>>)
при попытке отловить исключения
Мой код выглядит следующим образом:
public async Task Run()
{
var en = GetResources(new []{"a","b","c","d"});
await foreach (var item in en)
{
var res = item.Select(x => x.Id).ToArray();
System.Console.WriteLine(string.Join("-> ", res));
}
}
private async IAsyncEnumerable<IEnumerable<ResponseObj>> GetResources(
IEnumerable<string> identifiers)
{
IEnumerable<IEnumerable<string>> groupedIds = identifiers.Batch(2);
// MoreLinq extension method -- batches IEnumerable<T>
// into IEnumerable<IEnumerable<T>>
foreach (var batch in groupedIds)
{
//GetHttpResource is simply a wrapper around HttpClient which
//makes an Http request to an API endpoint with the given parameter
var tasks = batch.Select(id => ac.GetHttpResourceAsync(id)).ToList();
// if I remove this ToList(), the behavior changes
var stats = tasks.Select(t => t.Status);
// at this point the status being WaitingForActivation is reasonable
// since I have not awaited yet
IEnumerable<ResponseObj> res = null;
var taskGroup = Task.WhenAll(tasks);
try
{
res = await taskGroup;
var awaitedStats = tasks.Select(t => t.Status);
//this is the part that changes
//if I have .ToList(), the statuses are RanToCompletion or Faulted
//if I don't have .ToList(), the statuses are always WaitingForActivation
}
catch (Exception ex)
{
var exceptions = taskGroup.Exception.InnerException;
DoSomethingWithExceptions(exceptions);
res = tasks.Where(g => !g.IsFaulted).Select(t => t.Result);
//throws an exception because all tasks are WaitingForActivation
}
yield return res;
}
}
В конечном счете, я у меня есть IEnumerable
идентификаторов, я делю это на партии по 2 (жестко запрограммированные в этом примере), а затем запускаю Task.WhenAll
для запуска каждой партии по 2 одновременно.
Что я хотите, если 1 из 2 GetResource
задач завершается неудачно, все равно возвращает успешный результат другой и обрабатывает исключение (скажем, записывает его в журнал).
Если я запускаю Task.WhenAll
в список задач, это работает именно так, как я хочу. Однако, если я удаляю .ToList()
, когда я пытаюсь найти свои неисправные задачи в блоке перехвата после await taskGroup
, я сталкиваюсь с проблемами, потому что статусы моих задач все еще WaitingForActivation
, хотя я полагаю, что они ожидались .
Когда не выдается исключение, List
и IEnumerable
действуют одинаково. Это только начинает вызывать проблемы, когда я пытаюсь перехватить исключения.
В чем причина такого поведения? Task.WhenAll
должно быть завершено с тех пор, как я попал в блок catch, но почему статусы все еще WaitingForActivation
? Неужели я не смог asp что-то фундаментальное здесь?