Cara: Menangani Pengecualian dalam Kueri PLINQ

Contoh pertama dalam topik ini menunjukkan cara menangani System.AggregateException yang dapat dilemparkan dari kueri PLINQ saat dijalankan. Contoh kedua menunjukkan cara menempatkan blok try-catch dalam delegasi, sedekat mungkin dengan tempat pengecualian akan dilemparkan. Dengan cara ini, Anda dapat menangkapnya segera setelah terjadi dan mungkin melanjutkan eksekusi kueri. Ketika pengecualian diizinkan untuk naik kembali ke alur untuk bergabung, maka ada kemungkinan bahwa kueri dapat terus memproses beberapa item setelah pengecualian dimunculkan.

Dalam beberapa kasus ketika PLINQ kembali ke eksekusi berurutan, dan pengecualian terjadi, pengecualian dapat disebarluaskan secara langsung, dan tidak dibungkus dalam AggregateException. Juga, ThreadAbortExceptionselalu disebarluaskan secara langsung.

Catatan

Ketika "Just My Code" diaktifkan, Visual Studio akan berhenti pada baris yang mengeluarkan pengecualian dan menampilkan pesan kesalahan yang mengatakan "pengecualian tidak ditangani oleh kode pengguna." Kesalahan ini tidak berbahaya. Anda dapat menekan F5 untuk melanjutkannya, dan melihat perilaku penanganan pengecualian yang ditunjukkan dalam contoh di bawah ini. Untuk mencegah Visual Studio melanggar kesalahan pertama, cukup hapus centang pada kotak centang "Hanya Kode Saya" di bawah Alat, Opsi, Penelusuran Kesalahan, Umum.

Contoh ini dimaksudkan untuk menunjukkan penggunaan dan mungkin tidak berjalan lebih cepat daripada kueri LINQ berurutan yang setara ke kueri Objek. Untuk informasi selengkapnya, lihat Memahami Percepatan di PLINQ.

Contoh 1

Contoh ini menunjukkan cara menempatkan blok try-catch di sekitar kode yang menjalankan kueri untuk menangkap apa pun System.AggregateException yang dilemparkan.

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

Dalam contoh ini, kueri tidak dapat dilanjutkan setelah pengecualian dilemparkan. Pada saat kode aplikasi Anda menangkap pengecualian, PLINQ telah menghentikan kueri pada semua utas.

Contoh 2

Contoh berikut menunjukkan cara menempatkan blok try-catch dalam delegasi untuk memungkinkan menangkap pengecualian dan melanjutkan eksekusi kueri.

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

Mengompilasi Kode

  • Untuk mengkompilasi dan menjalankan contoh ini, salin ke dalam contoh Sampel Data PLINQ dan panggil metode dari Utama.

Pemrograman yang Kuat

Jangan menangkap pengecualian kecuali Anda tahu cara menanganinya sehingga Anda tidak merusak status program Anda.

Lihat juga