Megosztás a következőn keresztül:


Útmutató: Kivételek kezelése PLINQ-lekérdezésekben

A jelen témakör első példája bemutatja, hogyan kezelhetők a System.AggregateException PLINQ-lekérdezésekből a végrehajtáskor eldobható adatok. A második példa bemutatja, hogyan helyezheti el a kipróbálási blokkokat a meghatalmazottakon belül, a lehető legközelebb ahhoz, ahol a kivétel ki lesz dobva. Ily módon azonnal elkaphatja őket, és folytathatja a lekérdezések végrehajtását. Ha a kivételek visszaugranak az összekapcsolt szálra, lehetséges, hogy a lekérdezés a kivétel felmerülése után is feldolgoz néhány elemet.

Bizonyos esetekben, amikor a PLINQ visszaesik a szekvenciális végrehajtásra, és kivétel történik, a kivétel közvetlenül propagálható, és nem burkolva egy AggregateException. Emellett az ThreadAbortExceptions-eket mindig közvetlenül propagálja a rendszer.

Feljegyzés

Ha a "Just My Code" engedélyezve van, a Visual Studio megtörik a kivételt jelző sorban, és megjelenik egy hibaüzenet, amely azt jelzi, hogy "a felhasználói kód által nem kezelt kivétel". Ez a hiba jóindulatú. A folytatáshoz nyomja le az F5 billentyűt, és tekintse meg az alábbi példákban bemutatott kivételkezelési viselkedést. Ha meg szeretné akadályozni, hogy a Visual Studio feltörje az első hibát, törölje a jelet a "Just My Code" (Csak saját kód) jelölőnégyzetből az Eszközök, Beállítások, Hibakeresés, Általános területen.

Ez a példa a használat bemutatására szolgál, és előfordulhat, hogy nem fut gyorsabban, mint az objektumokhoz tartozó, szekvenciális LINQ lekérdezés. További információ a gyorsításról: Understanding Speedup in PLINQ.

1. példa

Ez a példa bemutatja, hogyan helyezheti el a try-catch blokkokat a lekérdezést végrehajtó kód köré a dobott s-ek elfogásához System.AggregateException.

// 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

Ebben a példában a lekérdezés nem folytatható a kivétel eldobása után. Mire az alkalmazáskód elfogja a kivételt, a PLINQ már leállította a lekérdezést az összes szálon.

2. példa

Az alábbi példa bemutatja, hogyan helyezhet el egy próbafogási blokkot egy delegáltban annak érdekében, hogy kivételt észleljen, és folytassa a lekérdezés végrehajtását.

// 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

A kód összeállítása

  • A példák fordításához és futtatásához másolja őket a PLINQ-adatmintába, és hívja meg a metódust a Mainból.

Robusztus programozás

Ne kapjon kivételt, hacsak nem tudja, hogyan kell kezelni, hogy ne sérüljön a program állapota.

Lásd még