Cómo: Controlar excepciones en una consulta PLINQ

En el primer ejemplo de este tema se muestra cómo controlar la excepción System.AggregateException que se puede iniciar desde una consulta PLINQ cuando se ejecuta. En el segundo ejemplo se muestra cómo incluir bloques try-catch dentro de los delegados, lo más próximos posibles al punto donde se iniciará la excepción. De esta manera, se pueden detectar en cuanto se producen y, posiblemente, continuar con la ejecución de la consulta. Cuando las excepciones pueden propagarse de nuevo al subproceso de unión, es posible que una consulta continúe procesando algunos elementos después de que se haya producido la excepción.

En algunos casos, cuando PLINQ utiliza la ejecución secuencial y se produce una excepción, es posible que esta se propague directamente y no se encapsule en una excepción AggregateException. Además, las excepciones ThreadAbortException siempre se propagan directamente.

NotaNota

Cuando está habilitada la opción "Solo mi código", Visual Studio se interrumpe en la línea que produce la excepción y muestra el mensaje de error "Excepción no controlada por el código de usuario". Este error es benigno.Puede presionar F5 para continuar y ver el comportamiento de control de excepciones que se muestra en los ejemplos siguientes.Para evitar que Visual Studio se interrumpa con el primer error, desactive la casilla "Solo mi código" bajo Herramientas, Opciones, Depuración, General.

Este ejemplo está diseñado para mostrar el uso y podría no ejecutarse más rápidamente que la consulta secuencial equivalente de LINQ to Objects.Para obtener más información sobre la aceleración, vea Introducción a la velocidad en PLINQ.

Ejemplo

En este ejemplo se muestra cómo colocar los bloques try-catch alrededor del código que ejecuta la consulta para detectar las excepciones System.AggregateExceptions que se produzcan.

' 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.");
        }
    }
}

En este ejemplo, la consulta no puede continuar una vez producida la excepción. Cuando el código de aplicación detecta la excepción, PLINQ ya ha detenido la consulta en todos los subprocesos.

En el siguiente ejemplo se muestra cómo colocar un bloque try-catch en un delegado para permitir que se detecte una excepción y que continúe la ejecución de la consulta.

' 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);
    }
}

Compilar el código

  • Para compilar y ejecutar estos ejemplos, cópielos en el ejemplo de Ejemplo de datos de PLINQ y llame al método desde Main.

Programación eficaz

No detecte ninguna excepción a menos que sepa cómo controlarla de forma que no dañe el estado del programa.

Vea también

Referencia

ParallelEnumerable

Conceptos

Parallel LINQ (PLINQ)

Historial de cambios

Fecha

Historial

Motivo

Mayo de 2010

Se ha agregado una nota sobre el uso frente a aceleración.

Comentarios de los clientes.