Task.WhenAll в списке <Task>ведет себя иначе, чем Task.WhenAll в IEnumerable <Task> - PullRequest
0 голосов
/ 06 апреля 2020

Я вижу некоторые странные поведенческие различия при вызове 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 что-то фундаментальное здесь?

1 Ответ

2 голосов
/ 07 апреля 2020

Если вы не сделаете список конкретным (используя ToList()), каждый раз, когда вы перечисляете список, вы снова вызываете GetHttpResourceAsync и создаете новую задачу. Это связано с отложенным выполнением .

. Я определенно буду удерживать вызов ToList() при работе со списком задач

...