Совокупное исключение, не содержащее всех исключений задачи? - PullRequest
1 голос
/ 17 августа 2011

В настоящее время я создаю демонстрационный проект, чтобы показать остальным членам моей команды, как они могут использовать TPL для создания лучшего кода.Тем не менее, я озадачен одной проблемой, которая, по моему мнению, должна работать по-другому;код:

// Example 7 - Even more exceptions

try
{
    var numberList = Enumerable.Range(0, 1000).AsParallel().Select(x =>
        {
            if (x % 2 == 0)
                throw new ApplicationException("Shazam!");

            return x;
        }).ToList();
}
catch (AggregateException e)
{
    int exceptionsAggregated = 0;
    e.Flatten().Handle(ex =>
        {
            if (ex is ApplicationException)
            {
                if ((ex as ApplicationException).Message == "Shazam!")
                    exceptionsAggregated++;
            }

            return true;
        });

    Console.WriteLine("Exceptions: " + exceptionsAggregated);
}

Я хотел бы, чтобы исключение составило 500 внутренних исключений, потому что каждый другой поток, вызванный в PLINQ, генерировал исключение.Тем не менее, я получаю только 4 исключения в совокупном исключении.

Сначала я подумал, что, возможно, TPL прекращает выполнение, когда достигает предела числа исключений, которые могут быть сгенерированы.Тем не менее, я не могу найти какие-либо онлайн-статьи или документацию, которые поддержали бы эту претензию.Так что я немного озадачен здесь;что могло послужить причиной только 4 исключений?

Я просто что-то упустил здесь что-то фундаментальное?

РЕДАКТИРОВАТЬ: @Dan Брайант ниже прикрепил его на голову;когда я изменяю код на следующее:

// Пример 7. Еще больше исключений

try
{
    var tasks = Enumerable.Range(0, 100).Select(x => Task.Factory.StartNew(() =>
        {
            if (x % 2 == 0)
                throw new ApplicationException("Shazam!");

            return x;
        })).ToArray();

    Task.WaitAll(tasks);
}
catch (AggregateException e)
{
    int exceptionsAggregated = 0;
    e.Flatten().Handle(ex =>
        {
            if (ex is ApplicationException)
            {
                if ((ex as ApplicationException).Message == "Shazam!")
                    exceptionsAggregated++;
            }

            return true;
        });

    Console.WriteLine("Exceptions: " + exceptionsAggregated);
}

Я правильно получаю правильное количество исключений.Проблема решена!

Ответы [ 2 ]

1 голос
/ 17 августа 2011

Задачи перестают быть расписанием задач после обнаружения исключений.Он возвращает исключения, которые он имеет до этого момента.Существующим задачам разрешено завершать (если они могут), и вы получаете только исключения для задач, которые фактически выполнялись.Любые задачи, которые все еще ожидали выполнения, не начинаются.(Помните, что задачи не обязательно начинаются сразу)

Вот еще информация, которую я написал в блоге несколько месяцев назад: http://colinmackay.co.uk/blog/2011/02/14/parallelisation-in-net-40-part-2-throwing-exceptions/

И поскольку вы используете PLINQ, вам также следуетПомните, что исключения не будут пузыриться в вызывающем потоке, пока вы не вызовете что-то, что вызывает WaitAll.Дополнительная информация: http://colinmackay.co.uk/blog/2011/05/16/tasks-that-throw-exceptions/

1 голос
/ 17 августа 2011

Одна возможность состоит в том, что параллельный выбор создает родительскую задачу с дочерней задачей для каждой итерации. TaskScheduler по умолчанию будет выполнять только определенное количество задач одновременно; если какая-либо из этих дочерних задач завершится неудачно, родительская задача не будет выполнена, что означает, что дочерние задачи, которые еще не были запущены, не будут выполняться.

Это соответствует концепции, что Select не должен иметь побочных эффектов, поскольку сбой в середине Select не позволит последующим вызовам перечисления прекратить выполнение. Разница с версией Parallel заключается в том, что может возникнуть несколько исключений (из-за частично параллельного выполнения), тогда как «последовательный» выбор может вызвать только одно исключение при перечислении.

Другая возможность состоит в том, что он создает фиксированное количество задач, а затем распределяет работу для них через параллельные блокирующие коллекции. После сбоя каждой задачи она прекращает выполнение назначенной ей рабочей нагрузки. Я думаю, что это последнее объяснение на самом деле более вероятно, но я должен изучить реализацию, чтобы знать наверняка.

...