Поделиться через


Практическое руководство. Обработка исключений в запросе PLINQ

В первом примере этого раздела показано, как обработать исключение System.AggregateException, которое может быть создано из запроса PLINQ при его выполнении. Во втором примере показано, как поместить блоки try-catch в делегаты максимально близко к месту создания исключения. Таким образом их можно перехватить, как только они появятся, и, возможно, продолжить выполнение запроса. Если позволить исключениям маршрутизироваться по восходящей обратно в присоединяемый поток, запрос может продолжить обработку некоторых элементов после создания исключения.

В некоторых случаях, если PLINQ возвращается к последовательному выполнению и происходит исключение, оно может распространяться напрямую, а не заключенное в оболочку AggregateException. Кроме того, исключения ThreadAbortException всегда распространяются напрямую.

ПримечаниеПримечание

Если включен режим "Только мой код", Visual Studio прервет выполнение программы на строке, в которой создается исключение, и отобразит сообщение об ошибке "Исключение, которое не может быть обработано пользовательским кодом". Эта ошибка не является критической.Можно нажать F5, чтобы продолжить выполнение с этой ошибки. См. поведение системы при обработке этого исключения в примерах ниже.Чтобы предотвратить прерывание выполнения после первой ошибки в Visual Studio, необходимо снять флажок "Только мой код" в меню Сервис, Параметры, Отладка, Общие.

Этот пример демонстрирует использование и может выполняться медленнее, чем аналогичный последовательный запрос LINQ to Objects.Дополнительные сведения об увеличении скорости см. в разделе Общее представление об ускорении выполнения в PLINQ.

Пример

В этом примере показано, как поместить блоки try-catch рядом с кодом, который выполняет запрос для перехвата любых созданных исключений System.AggregateException.

' Paste into PLINQDataSample class
Shared Sub PLINQExceptions_1()

    ' Using the raw string array here. See PLINQ Data Sample.
    Dim customers As String() = GetCustomersAsStrings().ToArray()

    ' First, we must simulate some currupt input.
    customers(20) = "###"

    'throws indexoutofrange
    Dim query = From cust In customers.AsParallel() _
        Let fields = cust.Split(","c) _
        Where fields(3).StartsWith("C") _
        Select fields
    Try
        ' We use ForAll although it doesn't really improve performance
        ' since all output is serialized through the Console.
        query.ForAll(Sub(e)
                         Console.WriteLine("City: {0}, Thread:{1}")
                     End Sub)
    Catch e As AggregateException

        ' In this design, we stop query processing when the exception occurs.
        For Each ex In e.InnerExceptions
            Console.WriteLine(ex.Message)
            If TypeOf ex Is IndexOutOfRangeException Then
                Console.WriteLine("The data source is corrupt. Query stopped.")
            End If
        Next
    End Try
End Sub
// Paste into PLINQDataSample class.
static void PLINQExceptions_1()
{
    // Using the raw string array here. See PLINQ Data Sample.
    string[] customers = GetCustomersAsStrings().ToArray();


    // First, we must simulate some currupt input.
    customers[54] = "###";

    var parallelQuery = from cust in customers.AsParallel()
                        let fields = cust.Split(',')
                        where fields[3].StartsWith("C") //throw indexoutofrange
                        select new { city = fields[3], thread = Thread.CurrentThread.ManagedThreadId };
    try
    {
        // We use ForAll although it doesn't really improve performance
        // since all output is serialized through the Console.
        parallelQuery.ForAll(e => Console.WriteLine("City: {0}, Thread:{1}", e.city, e.thread));
    }

    // In this design, we stop query processing when the exception occurs.
    catch (AggregateException e)
    {
        foreach (var ex in e.InnerExceptions)
        {
            Console.WriteLine(ex.Message);
            if (ex is IndexOutOfRangeException)
                Console.WriteLine("The data source is corrupt. Query stopped.");
        }
    }
}

В этом примере запрос не может быть продолжен после создания исключения. К моменту перехвата кодом приложения исключения PLINQ уже останавливает выполнение запроса во всех потоках.

В следующем примере показано, как поместить блок try-catch в делегат, чтобы сделать возможным перехват исключения и продолжение выполнения запроса.

' Paste into PLINQDataSample class
Shared Sub PLINQExceptions_2()

    Dim customers() = GetCustomersAsStrings().ToArray()
    ' Using the raw string array here.
    ' First, we must simulate some currupt input
    customers(20) = "###"

    ' Create a delegate with a lambda expression.
    ' Assume that in this app, we expect malformed data
    ' occasionally and by design we just report it and continue.
    Dim isTrue As Func(Of String(), String, Boolean) = Function(f, c)

                                                           Try

                                                               Dim s As String = f(3)
                                                               Return s.StartsWith(c)

                                                           Catch e As IndexOutOfRangeException

                                                               Console.WriteLine("Malformed cust: {0}", f)
                                                               Return False
                                                           End Try
                                                       End Function

    ' Using the raw string array here
    Dim query = From cust In customers.AsParallel()
                        Let fields = cust.Split(","c)
                        Where isTrue(fields, "C")
                       Select New With {.City = fields(3)}
    Try
        ' We use ForAll although it doesn't really improve performance
        ' since all output must be serialized through the Console.
        query.ForAll(Sub(e) Console.WriteLine(e.city))


        ' IndexOutOfRangeException will not bubble up      
        ' because we handle it where it is thrown.
    Catch e As AggregateException
        For Each ex In e.InnerExceptions
            Console.WriteLine(ex.Message)
        Next
    End Try
End Sub
// Paste into PLINQDataSample class.
static void PLINQExceptions_2()
{

    var customers = GetCustomersAsStrings().ToArray();
    // Using the raw string array here.
    // First, we must simulate some currupt input
    customers[54] = "###";

    // Create a delegate with a lambda expression.
    // Assume that in this app, we expect malformed data
    // occasionally and by design we just report it and continue.
    Func<string[], string, bool> isTrue = (f, c) =>
    {
        try
        {
            string s = f[3];
            return s.StartsWith(c);
        }
        catch (IndexOutOfRangeException e)
        {
            Console.WriteLine("Malformed cust: {0}", f);
            return false;
        }
    };

    // Using the raw string array here
    var parallelQuery = from cust in customers.AsParallel()
                        let fields = cust.Split(',')
                        where isTrue(fields, "C") //use a named delegate with a try-catch
                        select new { city = fields[3] };
    try
    {
        // We use ForAll although it doesn't really improve performance
        // since all output must be serialized through the Console.
        parallelQuery.ForAll(e => Console.WriteLine(e.city));
    }

    // IndexOutOfRangeException will not bubble up      
    // because we handle it where it is thrown.
    catch (AggregateException e)
    {
        foreach (var ex in e.InnerExceptions)
            Console.WriteLine(ex.Message);
    }
}

Компиляция кода

  • Чтобы скомпилировать и запустить эти примеры, скопируйте их в пример данных PLINQ и вызовите метод из Main.

Отказоустойчивость

Не перехватывайте исключение, если неизвестно, как его обрабатывать, чтобы не нарушить состояние программы.

См. также

Ссылки

ParallelEnumerable

Основные понятия

Parallel LINQ (PLINQ)

Журнал изменений

Дата

Журнал

Причина

Май 2010

Добавлено примечание о сравнении использования и ускорения.

Обратная связь от клиента.