Postupy: Zpracování výjimek v dotazu PLINQ

První příklad v tomto tématu ukazuje, jak zpracovat System.AggregateException , který lze vyvolat z plINQ dotazu při jeho spuštění. Druhý příklad ukazuje, jak umístit bloky try-catch do delegátů, co nejblíže k místu, kde bude vyvolán výjimka. Tímto způsobem je můžete zachytit hned, jak k nim dojde, a případně pokračovat ve spouštění dotazů. Pokud jsou výjimky povolené, aby se vrátily zpět do spojovacího vlákna, je možné, že dotaz může po vyvolání výjimky pokračovat ve zpracování některých položek.

V některých případech, kdy PLINQ spadá zpět do sekvenčního spuštění a dojde k výjimce, může být výjimka rozšířena přímo a není zabalena do AggregateException. ThreadAbortExceptionNavíc se vždy šíří přímo.

Poznámka:

Pokud je povolená možnost Pouze můj kód, Sada Visual Studio se přeruší na řádku, který vyvolá výjimku, a zobrazí chybovou zprávu s informací, že "výjimka nezpracována uživatelským kódem". Tato chyba je neškodná. Stisknutím klávesy F5 můžete pokračovat a zobrazit chování zpracování výjimek, které je znázorněno v následujících příkladech. Pokud chcete sadě Visual Studio zabránit v přerušení první chyby, zrušte zaškrtnutí políčka Jen můj kód v části Nástroje, Možnosti, Ladění, Obecné.

Tento příklad je určený k předvedení využití a nemusí běžet rychleji než ekvivalentní sekvenční dotaz LINQ to Objects. Další informace o zrychlení naleznete v tématu Principy zrychlení v PLINQ.

Příklad 1

Tento příklad ukazuje, jak umístit bloky try-catch kolem kódu, který spouští dotaz, aby zachytil všechny System.AggregateExceptionvyvolány.

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

V tomto příkladu nemůže dotaz pokračovat po vyvolání výjimky. V době, kdy kód aplikace výjimku zachytí, už PLINQ zastavil dotaz ve všech vláknech.

Příklad 2

Následující příklad ukazuje, jak vložit blok try-catch do delegáta, aby bylo možné zachytit výjimku a pokračovat v provádění dotazu.

// Paste into PLINQDataSample class.
static void PLINQExceptions_2()
{
    var customers = GetCustomersAsStrings().ToArray();
    // Using the raw string array here.
    // First, we must simulate some corrupt input
    customers[54] = "###";

    // Assume that in this app, we expect malformed data
    // occasionally and by design we just report it and continue.
    static bool IsTrue(string[] f, string c)
    {
        try
        {
            string s = f[3];
            return s.StartsWith(c);
        }
        catch (IndexOutOfRangeException)
        {
            Console.WriteLine($"Malformed cust: {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);
        }
    }
}
' Paste into PLINQDataSample class
Shared Sub PLINQExceptions_2()

    Dim customers() = GetCustomersAsStrings().ToArray()
    ' Using the raw string array here.
    ' First, we must simulate some corrupt 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

Probíhá kompilace kódu

  • Pokud chcete tyto příklady zkompilovat a spustit, zkopírujte je do ukázky plINQ Data Sample a volejte metodu z Main.

Robustní programování

Nezachyťte výjimku, pokud nevíte, jak ji zpracovat, abyste nepoškodí stav programu.

Viz také