Parallel.Invoke - обработка исключений - PullRequest
6 голосов
/ 16 сентября 2010

Мой код запускает 4 функции для ввода информации (используя Invoke) в такой класс, как:

class Person
{
    int Age;
    string name;
    long ID;
    bool isVegeterian

    public static Person GetPerson(int LocalID)
    {
        Person person;
        Parallel.Invoke(() => {GetAgeFromWebServiceX(person)}, 
                        () => {GetNameFromWebServiceY(person)},
                        () => {GetIDFromWebServiceZ(person)},
                        () => 
                        {
                           // connect to my database and get information if vegeterian (using LocalID)
                           ....
                           if (!person.isVegetrian)
                               return null
                           ....
                        });
     }
}

Мой вопрос: я не могу вернуть ноль, если он не вегетарианец, но я хочу иметь возможность остановить все потоки, остановить обработку и просто вернуть ноль. Как этого добиться?

Ответы [ 3 ]

8 голосов
/ 16 сентября 2010

Чтобы выйти из Parallel.Invoke как можно раньше, вам нужно сделать три вещи:

  1. Запланируйте действие, которое определяет, хотите ли вы выйти раньше, чем первое действие. Затем он запланирован раньше (может быть, как первый, но это не гарантировано), поэтому вы скорее узнаете, хотите ли вы выйти.
  2. Создайте исключение при обнаружении ошибки и поймайте AggregateException, как указывает ответ Джона.
  3. Используйте токены отмены. Однако это имеет смысл, только если у вас есть возможность проверить их свойство IsCancellationRequested.

Ваш код будет выглядеть следующим образом:

var cts = new CancellationTokenSource();
try
{
    Parallel.Invoke(
        new ParallelOptions { CancellationToken = cts.Token },
        () =>
        {
            if (!person.IsVegetarian)
            {
                cts.Cancel();
                throw new PersonIsNotVegetarianException();
            }
        },
        () => { GetAgeFromWebServiceX(person, cts.Token) }, 
        () => { GetNameFromWebServiceY(person, cts.Token) },
        () => { GetIDFromWebServiceZ(person, cts.Token) }
    );
}
catch (AggregateException e)
{
    var cause = e.InnerExceptions[0];
    // Check if cause is a PersonIsNotVegetarianException.
}

Однако, как я уже сказал, токены отмены имеют смысл, только если вы можете их проверить. Поэтому внутри GetAgeFromWebServiceX должна быть возможность проверить токен отмены и выйти рано, иначе передача токенов этим методам не имеет смысла.

4 голосов
/ 16 сентября 2010

Что ж, вы можете выбросить исключение из вашего действия, поймать AggregateException в GetPerson (т. Е. Поместить блок try / catch вокруг Parallel.Invoke), проверить, является ли оно правильным видом исключения, и вернуть null.

Это выполняет все , кроме , останавливая все потоки.Я думаю, что маловероятно, что вы легко сможете остановить уже запущенных задач , если не начнете получать токены отмены.Вы можете остановить выполнение дальнейших задач, сохранив значение boolean, чтобы указать, не выпала ли какая-либо из задач на данный момент, и заставить каждую задачу проверять это перед запуском ... это несколько уродливо, но это будетработа.

Я подозреваю, что использование "полных" задач вместо Parallel.Invoke сделало бы все это более элегантным.

1 голос
/ 01 октября 2010

Конечно, вам сначала нужно загрузить Person из базы данных?Поскольку ваш код вызывает Web-сервисы с нулевым значением.

Если ваша логика действительно последовательная, делайте это последовательно и выполняйте только параллельно, что имеет смысл.

...